Checking for past purchases

If your app needs a list of digital goods that a user already purchased (for example, to avoid offering a digital good that the user already owns), you can use the PaymentManager::requestExistingPurchases() function. This function returns an ExistingPurchasesReply that lists the purchases that a user made. This list includes the purchases that a user made before an upgrade and the purchases that a user made after your app was removed and downloaded again.

Best practices

To minimize the number of requests to the Payment Service and limit your app's network and data usage, you can provide a way for the user to refresh existing purchases from the server in your UI. You can implement a button or action that allows the user to restore past purchases. You can call requestExistingPurchases(true) to request existing purchases from the BlackBerry World server when the button is tapped.

If you can't add a refresh option to your UI, you can call requestExistingPurchases(false) to request existing purchases from the BlackBerry World local cache (on the device) when your app starts. If the local cache is stale, the purchase history is automatically retrieved from the server.

You should store a record of the purchases that a user makes because digital goods represent features or goods in your app. You can use a QSettings object. The QSettings class provides persistent platform-independent application settings across sessions. You can use the values in QSettings to determine which features of your app should be available. You should update these settings when successful purchases are made, so that the user can use the new features right away.

There are a few scenarios where your QSettings can become out of sync:

  • The user makes a purchase on one device (for example, a BlackBerry PlayBook) then tries to use the purchase on another device (for example, a BlackBerry 10 smartphone).
  • The user restores a device to a backup made before certain purchases were made.

For these scenarios, it is a good practice to either provide a manual way for users to retrieve past purchases (by using a button or menu item on the screen) or perform periodic queries using getExistingPurchases(true).

Checking for past purchases in QML

In the following QML sample, a PaymentManager instance is added as an attached object. When the NavigationPane that represents the UI of the app emits the creationCompleted() signal, existing purchases are retrieved from the local cache and then requestExistingPurchases(false) is called to request purchases from the BlackBerry World cache.

import bb.cascades 1.2
import bb.platform 1.2

NavigationPane {
    id: navigationPane

    attachedObjects: [
        PaymentManager {
            id: rootPaymentManager
            applicationIconUrl: "http://mycompany.com/100x100.png"
            onExistingPurchasesFinished: {
                if (reply.errorCode == 0) {
                    // Store the purchases
                } else {
                    console.log("Error: " + reply.errorText);
                }
            }
        }
    ]
    Page {
        // Set up the UI of your app with a store and a game to play.
    }
    onCreationCompleted: {
        rootPaymentManager.setConnectionMode(0);
        // As soon as the page loads, retrieve any 
        // past purchases from local store then cache.
        PurchaseStore.retrieveLocalPurchases();
        rootPaymentManager.requestExistingPurchases(false);
    }
}

The code sample above uses a function defined in C++ to retrieve existing purchases from the local cache.

// Grab all purchases from cache and emit a signal for each. 
// The signal makes it very easy for our app to listen for
// each good found.

void PurchaseStore::retrieveLocalPurchases() {
	QStringList listOfDigitalGoodSkus = m_digitalGoodsStore.value(
			"digitalGoods").toStringList();
	if (!listOfDigitalGoodSkus.isEmpty()) {
	foreach(QString digitalGoodSku, listOfDigitalGoodSkus) {
		emit purchaseRetrieved(digitalGoodSku);
		}
	}
}

You can see this technique in more detail in the Freemium sample app.

Checking for past purchases in C++

The following C++ code sample requests the list of existing purchases by using the requestExistingPurchases() function, passing in a Boolean value to refresh the data from the server. The response is returned as an ExistingPurchasesReply. Then the function connects the finished() signal to a handleExistingPurchases() function to handle the finished() signal. The handleExistingPurchases() function should be declared in your MyPaymentApp.hpp file and implemented in your MyPayment.cpp file.

void MyPaymentApp::getExistingPurchases(bool refresh)
{
    //Request the existing purchases from the server.
    const ExistingPurchasesReply *reply = paymentManager->
        requestExistingPurchases(refresh);

    // Connect the finished() signal to a slot to 
    // handle the existing purchases.
        
    bool success = QObject::connect(reply, 
         SIGNAL(finished()),
         this,
         SLOT(handleExistingPurchases())); 

    if (success) {
       // Signal was successfully connected.

    } else {
       // Failed to connect to signal.
    }                          
}

You can generate a receipt for each purchase by using the ExistingPurchasesReply class. Here's a code sample of a function that finds existing purchases and produces a readable string of each receipt. The function that converts the PurchaseReceipt to a displayable string is the same function that was shown in Responding to a purchase.

void MyPaymentApp::handleExistingPurchases()
{
    bb::platform::ExistingPurchasesReply *reply = 
        qobject_cast<bb::platform::ExistingPurchasesReply*>(sender());
    Q_ASSERT(reply);

    // Check to see if the request finished.
    if (reply->isFinished()) {

    	// Emit an error signal if there were errors.
        if (reply->isError()) {
            emit infoResponseError(reply->errorCode(), 
                reply->errorText());

        //Emit a success signal if there were no errors.
        } else {

            const QList<PurchaseReceipt> receipts = 
                reply->purchases();

            if (receipts.isEmpty()) {
                emit existingPurchasesResponseSuccess("(No purchases)");
            } else {
                // For each purchase, emit a signal that 
                // represents the receipt for that purchase.
                Q_FOREACH(PurchaseReceipt r, receipts) {
                    emit skuPurchased(r);
                }
            }
        }
    } else {
       qDebug() << "Request existing purchases request didn't finish.";
    }
    reply->deleteLater();
}

You can see this technique in more detail in the Payment Service sample app.

Last modified: 2013-12-21



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus