In this article

Purchasing Consumable Products in Xamarin.iOS

03/18/2017

8 minutes to read

Contributors

In this article

Consumable products are the simplest to implement, since there is no
‘restore’ requirement. They are useful for products like in-game currency or
a single-use piece of functionality. Users can re-purchase consumable products
over-and-over again.

Built-In Product Delivery

The sample code accompanying this document demonstrates built-in products –
the Product IDs are hardcoded into the application because they are tightly
coupled to the code that ‘unlocks’ the feature after payment. The purchasing
process can be visualized like this:

The basic workflow is:

The app adds an SKPayment to the queue. If required the user will be prompted for
their Apple ID, and asked to confirm the payment.

StoreKit sends the
request to the server for processing.

When the transaction is
complete, the server responds with a transaction receipt.

The SKPaymentTransactionObserver subclass receives the receipt and
processes it.

The application enables the product (by updating NSUserDefaults or some other mechanism), and then calls
StoreKit’s FinishTransaction.

There is another type of workflow – Server-Delivered Products –
that is discussed later in the document (see the section Receipt Verification and Server-Delivered Products).

Consumable Products example

The InAppPurchaseSample code contains a project called Consumables that implements a basic
‘in-game currency’ (called “monkey credits”). The sample shows how to
implement two in-app purchase products to allow the user to buy as many
“monkey credits” as they wish – in a real application there would also be
some way of spending them!

The application is shown in these
screenshots – each purchase adds more “monkey credits” to the user’s
balance:

The interactions between custom classes, StoreKit and the App Store look
like this:

ViewController Methods

In addition to the properties and methods required to retrieve product
information, the view controller requires additional notification observers to
listen for purchase-related notifications. These are just NSObjects
that will be registered and removed in ViewWillAppear and ViewWillDisappear respectively.

NSObject succeededObserver, failedObserver;

The constructor will also create the SKProductsRequestDelegate
subclass ( InAppPurchaseManager) that in turn creates and registers
the SKPaymentTransactionObserver
( CustomPaymentObserver).

The first part of
processing an in-app purchase transaction is to handle the button press when the
user wishes to buy something, as shown in the following code from the sample
application:

In addition to these methods on the view controller, a consumable product
purchase transaction also requires code on the SKProductsRequestDelegate and the SKPaymentTransactionObserver.

InAppPurchaseManager Methods

The sample code implements a number of purchase related methods on the
InAppPurchaseManager class, including the PurchaseProduct method
that creates an SKPayment instance and adds it to the queue for
processing:

Adding the payment to the queue is an asynchronous operation. The application
regains control while StoreKit processes the transaction and sends it to
Apple’s servers. It is at this point that iOS will verify the user is logged
in to the App Store and prompt her for an Apple ID and password if required.

Assuming the user successfully authenticates with the App Store
and agrees to the transaction, the SKPaymentTransactionObserver
will receive StoreKit’s response and call the following method to fulfill the
transaction and finalize it.

public void CompleteTransaction (SKPaymentTransaction transaction)
{
var productId = transaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
PhotoFilterManager.Purchase(productId);
FinishTransaction(transaction, true);
}

The last step is to ensure that you notify StoreKit that you have
successfully fulfilled the transaction, by calling FinishTransaction:

Once the product is delivered, SKPaymentQueue.DefaultQueue.FinishTransaction must be called to
remove the transaction from the payment queue.

SKPaymentTransactionObserver (CustomPaymentObserver) Methods

StoreKit calls the UpdatedTransactions method when it receives a
response from Apple’s servers, and passes an array of SKPaymentTransaction objects for your code to inspect. The method
loops through each transaction and performs a different function based on the
transaction state (as shown here):

Because a network connection can be transient on mobile devices, applications
may wish to monitor network status using the SystemConfiguration framework, and
re-try when a network connection is available. Refer to Apple’s or the that
uses it.

Purchase Transactions

The StoreKit payment queue will store and forward purchase requests if
possible, so the effect of a network outage will vary depending on when the
network failed during the purchase process.

If an error does
occur during a transaction, the SKPaymentTransactionObserver
subclass ( CustomPaymentObserver) will have the UpdatedTransactions method called and the SKPaymentTransaction class will be in the Failed state.

Even if a transaction fails, the FinishTransaction method must
be called to remove the transaction from the payment queue:

SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);

The example code then sends a notification so that the ViewController can
display a message. Applications should not show an additional message if the
user canceled the transaction. Other error codes that might occur include:

FailedTransaction Code=0 Cannot connect to iTunes Store
FailedTransaction Code=5002 An unknown error has occurred
FailedTransaction Code=5020 Forget Your Password?
Applications may detect and respond to specific error codes, or handle them in the same way.

Handling Restrictions

The Settings > General > Restrictions feature of iOS
allows users to lock certain features of their device.

You
can query whether the user is allowed to make in-app purchases via the SKPaymentQueue.CanMakePayments method. If this returns false then
the user cannot access in-app purchasing. StoreKit will automatically display an
error message to the user if a purchase is attempted. By checking this value
your application can instead hide the purchase buttons or take some other action
to help the user.

In the InAppPurchaseManager.cs
file the CanMakePayments method wraps the StoreKit function like
this:

To test this method, use the Restrictions feature of iOS to
disable In-App Purchases:

This example code from ConsumableViewController reacts to CanMakePayments returning false by displaying AppStore Disabled text on the disabled buttons.

// only if we can make payments, request the prices
if (iap.CanMakePayments()) {
// now go get prices, if we don't have them already
if (!pricesLoaded)
iap.RequestProductData(products); // async request via StoreKit -> App Store
} else {
// can't make payments (purchases turned off in Settings?)
// the buttons are disabled by default, and only enabled when prices are retrieved
buy5Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
buy10Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
}

The application looks like this when the In-App Purchases
feature is restricted – the purchase buttons are disabled.

Product information can still be requested when CanMakePayments is
false, so the app can still retrieve and display prices. This means if we
removed the CanMakePayments check from the code the purchase
buttons would still be active, however when a purchase is attempted the user
will see a message that In-app purchases are not allowed
(generated by StoreKit when the payment queue is accessed):

Real-world applications may take a different approach to handling the
restriction, such as hiding the buttons altogether and perhaps offering a more
detailed message than the alert that StoreKit shows automatically.

Feedback

We'd love to hear your thoughts. Choose the type you'd like to provide: