Creating the digital good component

Screen showing a digital good in the Freemium sample app store.You can use the DigitalGood custom component for any digital good. Each digital good is defined with the same characteristics in the DigitalGood.qml file. Each digital good has an ID plus a set of properties that define the SKU, the name, and an image for the digital good, as well as a property that tracks whether it is owned. You can see the layout of each digital good in the following sample:

import bb.cascades 1.0
import bb.platform 1.2

Container {
    attachedObjects: [

        PaymentManager {
            id: digitalGoodPaymentManager
            onPurchaseFinished: {
                if (reply.errorCode == 0) {
                   PurchaseStore.storePurchase(reply.digitalGoodSku);
                } else {
                   console.log("Error: " + reply.errorInfo);
                }
            }
        }
    ]

    // Giving this control an ID allows it to be referenced
    // explicitly from methods where context is lost.
    id: digitalGood

    // Basic list of properties exposed for assignment
    property bool owned: false 
    property string sku 
    property alias imageSource: digitalGoodImage.imageSource
    property alias name: digitalGoodName.text
    layout: DockLayout {
    }

    preferredHeight: 200
    preferredWidth: 250
    leftMargin: 25
    rightMargin: 25

    ImageView {
        imageSource: "asset:///images/backgroundFrame.png.amd"
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        opacity: 0.9
    }

    // A small icon representing the digital good
    ImageView {
        id: digitalGoodImage
        scalingMethod: ScalingMethod.AspectFit
        horizontalAlignment: HorizontalAlignment.Center
        verticalAlignment: VerticalAlignment.Center
    }

    // Space for the name of the Digital Good to be displayed
    Label {
        id: digitalGoodName
        horizontalAlignment: HorizontalAlignment.Center
        verticalAlignment: VerticalAlignment.Bottom
    }

    // A container to place on top of the digital  
    // good if it has not yet been purchased.

    // Handle the actions on the digital good
}

Screen showing a digital good in the Freemium sample app that has not been purchased yet.The DigitalGood component uses a Container that is overlaid on top of the digital good if it has not yet been purchased.

The visible property of this Container is connected to the owned property of the digital good so that the overlay is updated automatically when the digital good is purchased.

    
    Container {
        layout: DockLayout {
        }
        visible: ! owned
        opacity: 0.3
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        ImageView {
            imageSource: "asset:///images/backgroundFrame.png.amd"
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Fill
        }
        ImageView {
            imageSource: "asset:///images/lock.png"
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Center
        }
    }

The UI of the digital good includes a TapHandler that calls the requestPurchase() function when the tapped() signal is emitted.

    
    // Handle clicks by the user, 
    // normally symbolizing a purchase attempt
    gestureHandlers: TapHandler {
        onTapped: {
            if (digitalGood.owned) {
                // Do something, not needed for this sample
                // May be good to display a message to the user, 
                // or bring up details on the digital good
            } else {
                digitalGoodPaymentManager.requestPurchase("", 
                    digitalGood.sku, 
                    digitalGood.name);
            }
        }
    }

The rest of the DigitalGood component defines two local functions for checking whether a digital good has already been purchased and for resetting the ownership of a digital good to false. These two functions are connected to two signals that are defined in C++ in a PurchaseStore class.

    // Connect the important signals from the 
    // PurchaseStore to localized functions
    // within the DigitalGood class
    onCreationCompleted: {
        PurchaseStore.purchaseRetrieved.connect(purchaseMade);
        PurchaseStore.purchaseRecordsDeleted.connect(
            purchaseRecordsDeleted);
    }

    // Checks to see if the recent purchase was for this good
    function purchaseMade(digitalGoodSku) {
        digitalGood.owned |= (digitalGoodSku == digitalGood.sku);
    }

    // Wipes owenership field of this good if requested by the app
    function purchaseRecordsDeleted() {
        digitalGood.owned = false;
    }

The PurchaseStore class handles the caching and retrieval of purchases to and from QSettings. There is no interaction with the Payment Service from this class; it just stores and retrieves QString values that represent the SKUs of digital goods that are purchased.

To view the class definition for PurchaseStore, see the following code samples:

#ifndef PURCHASESTORE_HPP_
#define PURCHASESTORE_HPP_

#include <QObject>
#include <QSettings>
#include <Qt/qdeclarativedebug.h>

class PurchaseStore: public QObject {
	Q_OBJECT
public:
	PurchaseStore(QObject* parent = 0);
	virtual ~PurchaseStore();

public Q_SLOTS:
	void deletePurchaseRecords();
	void storePurchase(const QString& digitalGoodSku);
	void retrieveLocalPurchases();

	Q_SIGNALS:
		void purchaseRecordsDeleted();
		void purchaseRetrieved(const QString& digitalGoodSku);

private:
	// This sample stores all purchases in local persistent store to make goods readily
	//	accessible even without network access or relying on the cache.
	QSettings m_digitalGoodsStore;
};

#endif /* PURCHASESTORE_HPP_ */
#include "PurchaseStore.hpp"

#include <QStringList>

PurchaseStore::PurchaseStore(QObject* parent): QObject(parent) {
}

PurchaseStore::~PurchaseStore() {
}

/**
 * Save the digital good to the store if it has not already been purchased.
 */
void PurchaseStore::storePurchase(const QString& digitalGoodSku) {
	QStringList listOfDigitalGoodSkus = m_digitalGoodsStore.value(
			"digitalGoods", QStringList()).toStringList();
	if (!listOfDigitalGoodSkus.contains(digitalGoodSku)) {
		listOfDigitalGoodSkus.append(digitalGoodSku);
		m_digitalGoodsStore.setValue("digitalGoods", listOfDigitalGoodSkus);
	}
	emit purchaseRetrieved(digitalGoodSku);
}

/**
 * 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);
	}
}
}

/** Clear out all records of purchases. This is meant to only be used for testing
 *  purposes and should be removed when moving to the release builds.
 */
void PurchaseStore::deletePurchaseRecords() {
	m_digitalGoodsStore.setValue("digitalGoods", QStringList());
	emit purchaseRecordsDeleted();
}


Last modified: 2013-12-21



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

comments powered by Disqus