Would you like to tell us how we are doing?

You bet No thanks

Creating a basic application

You can use the PaymentServiceDemo.as sample application to learn how to create a basic application that sells digital goods. The sample application shows you how to perform the following tasks:
  • Set up a UI
  • Create a class to store the properties for your digital goods
  • Retrieve and display a list of digital goods
  • Initiate a purchase
  • Check for past purchases

Before you begin

Before you run your basic app, add the following SWCs through the steps:
  1. In Flash Builder, on the Package Explorer menu, right-click on your ActionScript Mobile Project.
  2. Select Properties.
  3. In the Properties for project name window, click on ActionScript Build Path.
  4. In Library path, click Add SWC.
    • Browse to (C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.6\sdks\3.6.0\frameworks\libs\rpc.swc) and click Ok.
    • Browse to (C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.6\sdks\3.6.0\frameworks\libs\framework.swc) and click Ok.
    • Browse to (C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.6\sdks\3.6.0\frameworks\locale\en_US\rpc_rb.swc) and click Ok.
  5. Click Ok.

Set up the UI

This task demonstrates how to create a simple UI that allows users to purchase digital goods from your application.

  1. Import the required classes.
    import flash.display.Sprite; 
    import flash.events.MouseEvent;  
    import net.rim.blackberry.payment.PaymentSystem;  
    import qnx.fuse.ui.buttons.LabelButton; 
    import qnx.fuse.ui.listClasses.List;
  2. Add the following statement to your class, directly before the class signature. This statement sets the stage width and height, the frame rate, and the background color of your application.
    [SWF(height="600", width="1024", frameRate="30", 
    backgroundColor="#FFFFFF")]
  3. Create an application framework by extending Sprite.
    class PaymentServiceDemo extends Sprite 
    {
  4. Declare a variable for PaymentSystem, which is the class that is required for invoking purchases. Declare variables for the list and buttons that you need to use to build your UI.
    private var paymentSystem:PaymentSystem;
    private var buyList:List;
    private var buyButton:LabelButton;
    private var refreshButton:LabelButton;
  5. Create a constructor for your class. In the constructor, initialize PaymentSystem and set the connection mode to local. Setting the connection mode to local prevents your application from processing purchases with the Payment Service server. All purchases are simulated within the application. In the constructor, invoke initializeUI().
    public function PaymentServiceDemo()     
    {
         paymentSystem = new PaymentSystem();
         paymentSystem.setConnectionMode(PaymentSystem.CONNECTION_MODE_LOCAL);
       
         initializeUI();     
    }
  6. Create the initializeUI function to setup and initialize the UI controls.
    private function initializeUI():void
        {
  7. In the initializeUI function, define the UI components, set the appropriate properties, and add the components to the stage. Set the selection mode on the list to permit single selections. Set an event listener on each button to listen for click events.
    buyList = new List();
    buyList.setPosition(0,300);
    buyList.width = stage.stageWidth;
    buyList.height = stage.stageHeight - 300;
    //buyList.columnWidth = 400;
    buyList.scrollable = true;
    buyList.allowDeselect = false;
    buyList.selectionMode = ListSelectionMode.SINGLE;
    this.addChild(buyList);              
             
    refreshButton = new LabelButton();
    refreshButton.setPosition(300, 100);
    refreshButton.width = 200;
    refreshButton.height = 80;
    refreshButton.label = "Refresh";
    refreshButton.addEventListener(MouseEvent.CLICK, refreshHandler);
    this.addChild(refreshButton);                     
    
    buyButton = new LabelButton();
    buyButton.setPosition(50, 100);
    buyButton.width = 200;
    buyButton.height = 80;
    buyButton.label = "Buy";
    buyButton.addEventListener(MouseEvent.CLICK, purchaseHandler);
    this.addChild(buyButton);     
    }
  8. Create the purchaseHandler function. This function runs whens the Buy button is pressed.
    private function purchaseHandler(event:MouseEvent):void     
    {         
         
    }
  9. Create the refreshHandler function. This function runs whens the Refresh button is pressed.
    private function refreshHandler(event:MouseEvent):void     
    {         
         
    }
  10. Run the application.

When you run the application, only the buttons appear on the screen. Before the list can appear, you must specify a data provider for the list and populate the list with data.

Create a class to store the properties for your digital goods

Creating a separate class to store the properties for your digital goods isn't a requirement, but when you're dealing with large numbers of digital goods that you want to update dynamically, it can help you manage the data more efficiently. In this task, you create a class that is designed to store the properties for the digital goods that your application offers.

  1. Create a new ActionScript class named DigitalGoodsInfo.
    package com.test.paymentapi { 
         
                public class DigitalGoodsInfo     
                {
  2. Create public variables for each of the properties for your digital goods.
    //public var name:String;         
           public var label:String;         
           public var sku:String;         
           public var metadata:String;         
           public var icon:String;         
           public var appName:String;
  3. Create the constructor for DigitalGoodsInfo. The constructor accepts strings for the name, sku, and metadata of the digital goods as parameters.
    public function DigitalGoodsInfo( name:String, sku:String, metadata:String )
    {
    
    }
  4. In the constructor, set the values for the properties of the class to the values of the parameters that the constructor receives. Set the values of the icon and appName properties to default values.
    {
        trace("in DigitalGoodsInfo.");
        this.label = name;
        this.sku = sku;
        this.metadata = metadata;
    			
        icon = "http://www.rim.com/products/appworld_3col.jpg";
        appName = "PaymentServiceDemo";			
    }

Retrieve and display a list of digital goods

Depending on your application, you might want to dynamically update the list of digital goods that your application offers. By maintaining a list of digital goods outside of your application, you can make updates without having to release a new version of your application. In this task, you:
  • Retrieve data from a remote server by using an HTTPService call.
  • Create an ArrayCollection of DigitalGoodsInfo objects.
  • Populate the List object that you previously created using the names of the digital goods.
  1. In PaymentServiceDemo, import the required classes. Make sure that you import the DigitalGoodsInfo class that you created.
    import mx.collections.ArrayCollection; 
    import mx.rpc.events.FaultEvent; 
    import mx.rpc.events.ResultEvent; 
    import mx.rpc.http.HTTPService; 
    import qnx.ui.data.DataProvider;
    import DigitalGoodInfo;
  2. In the body of the PaymentServiceDemo class, add declarations for ArrayCollection and HTTPService variables. The array collection is required for storing the value objects that contain data about the digital goods that your application offers.
    private var digitalGoods:ArrayCollection;
    private var httpService:HTTPService;
  3. In the initializeUI function, create an instance of the HTTPService class. Specify the URL for the location of the .xml file, and set event listeners to listen for ResultEvent.RESULT and FaultEvent.FAULT. HTTPService broadcasts one of these two events depending on whether the HTTPService call is successful or not. Invoke send() to send the HTTPService call.
    this.addChild(refreshButton);
    httpService = new HTTPService();         
    httpService.url = "http://docs.blackberry.com/digitalgoods.xml";
    httpService.addEventListener(ResultEvent.RESULT, resultHandler);
    httpService.addEventListener(FaultEvent.FAULT, faultHandler);
    httpService.send();     
    }
  4. Create the function resultHandler. This function runs when HTTPService broadcasts a result event.
    private function resultHandler(event:ResultEvent):void     
    {

    The ResultEvent object that this function receives has a result property, which contains the .xml data retrieved during the HTTPService call.

  5. In the resultHandler function, declare variables for an ArrayCollection and a DataProvider. Declare a variable for DigitalGoodsInfo, the class that you created to store the properties of digital goods.
    var remoteData:ArrayCollection;
    var data:DataProvider;         
    var dgObject:DigitalGoodsInfo;
  6. In the resultHandler function, populate the remoteData variable that you declared with the .xml data that the event.result property contains. In the following code sample, digitalgoods.digitalgood represents the hierarchy of the elements in the .xml file. The parent element in the .xml file is named <digitalgoods>, and it contains a series of children named <digitalgood>. The <digitalgood> elements contain the individual properties for each digital good. The result of the following code sample is that each index in the array collection is populated with an object that contains a digitalgood element and all of its children elements.
    remoteData = event.result.digitalgoods.digitalgood;

    For the complete contents of the digitalgoods.xml file, visit http://docs.blackberry.com/digitalgoods.xml

  7. In the resultHandler function, initialize the data provider, and set the data provider on the list that you created when you set up the UI. Declare digitalGoods as the array collection that you created to contain the DigitalGoodsInfo objects.
    data = new DataProvider();
    buyList.dataProvider = data;
    digitalGoods = new ArrayCollection();
  8. In the resultHandler function, create a for each loop that iterates over each object in the array collection.
    for each(var obj:Object in remoteData)
    {
  9. In the for each loop, create a new instance of DigitalGoodsinfo. Pass in the name, sku, and metadata from the current index of the array collection to the constructor. Add the new instance of DigitalGoodsinfo to the digitalGoods array collection. Add the name of the digital goods to the data provider for the list that you previously created.
    dgObject = new DigitalGoodsInfo(obj.name, obj.sku, obj.metadata);
    digitalGoods.addItem(dgObject);                 
    buyList.dataProvider.addItem({label: obj.name});
  10. After the for each loop has executed, and the names of the digital goods are all added to the list, set the current selection in the list to the first item.
    buyList.selectedIndex = 0;         
    }     
    }
  11. Create the function faultHandler. This function runs when HTTPService broadcasts a fault event. In the faultHandler function, create a trace statement that notifies you if the application is unable to retrieve the data from the .xml file.
    private function faultHandler(event:FaultEvent):void     
    {         
         trace("Unable to retrieve digital goods from the server.");     
    }
  12. Run the application.

    The list that you created is now populated with the names of the digital goods.

Invoke a purchase

When you're ready to charge your user's account for a purchase, call the PaymentSystem.purchase function. PaymentSystem.purchase takes a variety of arguments that include things like authentication with the Payment Service server, informatino about the digitals goods being purchased, and so on. To see a description of all the arguments, see Arguments for purchases.

In this task, you:
  • Define the functionality for the purchaseHandler function, which runs when the user presses the Buy button.
  • Set event listeners to listen for successful and unsuccessful purchases.
  • Define the functionality for when the purchase routine completes.
  1. In PaymentServiceDemo, import the required classes.
    import net.rim.blackberry.events.PaymentErrorEvent; 
    import net.rim.blackberry.events.PaymentSuccessEvent;
    import net.rim.blackberry.payment.PaymentSystem;
    import net.rim.blackberry.payment.Purchase;
  2. In the purchaseHandler function, declare a variable called index. Set the value of index to the index of the row in the list that is currently selected.
    private function purchaseHandler(event:MouseEvent):void
    {
        var index:int;         
        index = buyList.selectedIndex;
  3. In the purchaseHandler function, invoke PaymentSystem.purchase(). From the array collection of DigitalGoodsInfo objects, get the properties of the object that is at the position in the array collection equivalent to the position of the list item that is currently selected. Pass in the properties as arguments to purchase().
        paymentSystem.purchase(null,digitalGoods.getItemAt(index).sku, 
            digitalGoods.getItemAt(index).label, digitalGoods.getItemAt(index).metadata,
            digitalGoods.getItemAt(index).appName, digitalGoods.getItemAt(index).icon);     
    }
  4. In the constructor, add some event listeners to listen for successful and unsuccessful purchase attempts.
    paymentSystem.setConnectionMode(PaymentSystem.CONNECTION_MODE_LOCAL);
    paymentSystem.addEventListener(PaymentSuccessEvent.PURCHASE_SUCCESS, purchaseSuccessHandler);
    paymentSystem.addEventListener(PaymentErrorEvent.PURCHASE_ERROR, purchaseErrorHandler);          
    
    initializeUI();
  5. Create the purchaseSuccessHandler function. This function runs when PaymentSystem dispatches PaymentSuccessEvent.PURCHASE_SUCCESS.
    private function purchaseSuccessHandler (event:PaymentSuccessEvent):void     
        {

    The PaymentSuccessEvent object that this function receives has a purchase property, which contains details about the successful purchase.

  6. In the purchaseSuccessHandler function, declare a variable for Purchase, and set the value to the event.purchase property. Create a trace statement to display all of the details of the successful purchase.
    var purchase:Purchase = event.purchase
    trace("Purchase Success - " + purchase.date + " : " + purchase.digitalGoodID + " : " + purchase.digitalGoodSKU 
         + " : " + purchase.licenseKey + " : " + purchase.metaData  + " : " + purchase.transactionID);     
    }
    In production, you might want to display an alert to the user indicating that the purchase is a success.
  7. Create the purchaseErrorHandler function. This function runs when PaymentSystem dispatches PaymentErrorEvent.PURCHASE_ERROR.
    private function purchaseErrorHandler
    (event:PaymentErrorEvent):void     
        {

    The PaymentErrorEvent object that this function receives has an errorID property, which contains an integer representing the type of error, and a text property, which contains a human-readable error message.

  8. In the purchaseErrorHandler function, create a trace statement to display the error ID and the error message.
    trace("Purchase Error - " + event.errorID + " : " + event.text);     
    }

    In production, you might want to handle some errors differently from others. For example, if the purchase fails because the user cancels the purchase, you might not want to do anything, while if the purchase fails because of an error with the server, you might want to display an alert that indicates that.

  9. Run the application.
  10. After the application starts, select one of the digital goods and press Buy. If the connection mode for the application is set to PaymentSystem.CONNECTION_MODE_LOCAL, the confirmation dialog that you see is different from the dialog that users see when the application is in production. If the application is configured for testing, the dialog displays a drop-down list that allows you specify the result that you want for the test purchase. This enables you test the responses you can expect from the Payment Service so that you can test whether your application handles the responses the way you intend.

Check for existing purchases

Records of a user's past purchases are stored in a file on the device, and on the Payment Service server. Identifying a user's past purchases is necessary if you need to perform any of the following tasks:
  • Modify the UI of your application to reflect past purchases.
  • Implement a subscription model for your digital goods.
  • Redistribute digital goods to users that switch devices.

The PaymentSystem.getExistingPurchases function accepts a Boolean argument named allowRefresh. If you specify true for allowRefresh, the function tries to retrieve the purchase history from the Payment Service server.

The purchases history on the device could be out of date for a variety reasons, including a device switch. As a best practice, you should invoke getExistingPurchases(true) each time your application starts. If the user has switched devices, you can use the updated array of purchases to make sure that all of the user's past purchases are made available.

In this task, you invoke getExistingPurchases(false) to retrieve the array of existing purchases from the device.

  1. In the refreshHandler function, invoke getExistingPurchases(). Pass in false as an argument so that the application doesn't attempt to refresh the array of past purchases with the server.
    private function purchaseHandler(event:MouseEvent):void     
        {         
           paymentSystem.getExistingPurchases(false);     
        }

    If your application is configured for local testing, and you attempt to refresh the array of existing purchases with the server, getExistingPurchases throws an error.

  2. In the constructor, add event listeners to listen for successful and unsuccessful attempts at retrieving the array of existing purchases.
    paymentSystem.addEventListener(PaymentSuccessEvent.GET_EXISTING_PURCHASES_SUCCESS, getPurchasesSuccessHandler);
    paymentSystem.addEventListener(PaymentErrorEvent.GET_EXISTING_PURCHASES_ERROR, getPurchasesErrorHandler);          
    
    initializeUI();     
    }
  3. Create the getPurchasesSuccessHandler function. This function runs when PaymentSystem dispatches PaymentSuccessEvent.GET_EXISTING_PURCHASES_SUCCESS. The PaymentSuccessEvent object has an existingPurchases property that contains an array of Purchase objects. Each of those objects represent a successful purchase.
    private function getPurchasesSuccessHandler(event:PaymentSuccessEvent):void     
    {
  4. In the getPurchasesSuccessHandler function, declare a variable for Purchase. Declare a variable for an array, and set the value to the event.existingPurchases property. Create a for loop that iterates through each position in the array of purchases.
    var purchase:Purchase; 
    var pastPurchases:Array = event.existingPurchases; 
    
    for (var i:int = 0; i < pastPurchases.length; i++) 
    {
  5. In the for loop, get the Purchase object from the current position in the array. Create a trace statement to display all of the details of the successful purchase.
        purchase = pastPurchases[i]; 
        trace("Get Existing Purchases Success - " + purchase.date + " : " + purchase.digitalGoodID + " : " + 
               purchase.digitalGoodSKU + " : " + purchase.licenseKey + " : " + purchase.metaData + " : " +
               purchase.transactionID); 
        } 
    }
  6. Create the getPurchasesErrorHandler function. This function runs when PaymentSystem dispatches PaymentSuccessEvent.GET_EXISTING_PURCHASES_SUCCESS. Create a trace statement to display the error ID and error message associated with the event.
    private function getPurchasesErrorHandler(event:PaymentErrorEvent):void 
    { 
        trace("Get Existing Purchases Error - " + event.errorID + " : " + event.text); 
    }
  7. Debug the application.
  8. After the application starts, initiate a series of successful purchases.
  9. Press the Refresh button.

    The console displays information for each of the previous successful purchases.



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

comments powered by Disqus