Developing your application
Process flow: Purchasing digital goods
The process for purchasing digital goods is designed to be consistent for each payment type and for all BlackBerry® device users.
- A user clicks a purchase
option for digital goods in your application.

- The application creates a
PurchaseArguments object which contains information
about the digital goods.
PurchaseArgumentsBuilder arguments = new PurchaseArgumentsBuilder() .withDigitalGoodSku( "abc123" ) .withDigitalGoodName( "Adventures of a BlackBerry Java Developer" ) .withPurchasingAppName( "My eBooks" ) .withMetadata ( "ISBN 34560202010" ); - The application invokes
PaymentEngine.purchase(PurchaseArguments) to initiate
the purchase request and send information about the digital goods to the
Payment
Service server.
try { Purchase purchase = engine.purchase(arguments.build()); } catch (PaymentException e) { Dialog.inform(e.getMessage()); } - If the user isn't
logged in using a BlackBerry®
ID account, the
application prompts the user to provide login information without requiring that the user leave the application.

- The
application prompts the user to confirm the purchase using the default payment
type. The user can change the payment type or set up a new payment type.

- The Payment Service server verifies that the digital goods are valid and processes the purchase through the payment provider.
- Depending on the results of
the purchase attempt, one of the following events occurs:
- If the purchase request is successful, the Payment Service server returns a Purchase object to the application which contains details about the successful purchase. If the application utilizes a content server, the Payment Service server can send information about the successful purchase to the content server as well.
- If the purchase request is unsuccessful, the application throws an exception. For information about handling exceptions, see the API reference for the Payment Service SDK at www.blackberry.com/developers/docs/payment/1.0api.
- If the purchase attempt is successful, the application provides the digital goods to the user in the manner that you designed.
Verifying that in-app purchases are supported on the device
Before you present your users with any purchase options, you can verify if in-app purchases are available to a BlackBerry® device user. You can use the PaymentEngine.getInstance method to check for the presence of the BlackBerry App World™ storefront 2.1 (a requirement for in-app purchases) and to create a PaymentEngine object if the check is successful. If unsuccessful, your application can't invoke any of the Payment Service APIs.
You might want to design your application to restrict users from seeing purchase options for digital goods if they don't have BlackBerry App World 2.1 installed on their devices.
The following code sample demonstrates how to create a PaymentEngine object and verify that in-app purchases are supported on the device.
PaymentEngine engine = PaymentEngine.getInstance();
if (engine != null)
{
//code that is executed if in-app payments are available.
}
Retrieving information about past purchases
You can retrieve information about past purchases by invoking PaymentEngine.getExistingPurchases(boolean allowRefresh). A record of past purchases is necessary if you want to customize your application to reflect past purchases, or implement a subscription model that initiates recurring purchases.
Invoking getExistingPurchases() returns an array of Purchase objects. Each result in the array contains details about a purchase, such as the SKU, digital good ID, metadata to differentiate between digital goods that reference the same SKU, and the date of the purchase.
The allowRefresh parameter is a Boolean value that indicates that the application can retrieve the records of purchases from the Payment Service server, or that the application can only retrieve records that are stored on the BlackBerry® device. To retrieve records from the Payment Service requires the BlackBerry device user to log in using a BlackBerry® ID. To retrieve records from the device does not require that the user log in. In some cases, such as after a device switch, records of purchases might not be stored on the device, so it might necessary to query the Payment Service server. While the operation is running, the application displays a progress bar that indicates the estimated time remaining.
If you're testing your application locally, the application can only pull records from the device.
Initiating a purchase
Before your application can initiate purchases by invoking PaymentEngine.purchase(), you must create a PurchaseArguments object to represent the digital goods. The Payment Service API provides a builder class for creating a PurchaseArguments object.
You can invoke the appropriate methods on the PurchaseArgumentsBuilder object depending on the information that you need to provide with the purchase request. At the minimum, you must provide either the ID or the SKU for the digital goods, for a purchase to be authenticated. The SKU is a value that you specify when you register the digital goods in the vendor portal for the BlackBerry App World™ storefront. The ID is an identifier that is assigned to your digital goods through the vendor portal.
If the purchase request is successful, a Purchase object is returned which contains information about the purchase. If the purchase is unsuccessful, an exception is thrown.
Code sample: Creating a PurchaseArguments object and initiating a purchase
PurchaseArgumentsBuilder arguments = new PurchaseArgumentsBuilder()
.withDigitalGoodId( "1234" )
.withDigitalGoodName( "My Product" )
.withDigitalGoodSku( "Ab34t2eC" )
.withPurchasingAppName( "My Application" );
try
{
Purchase purchase = engine.purchase(arguments.build());
}
catch (IllegalArgumentException iae)
{
//code that is executed if an exception is thrown
}
catch (PaymentException pe)
{
//code that is executed if an exception is thrown
}
Arguments for purchase requests
The following table describes the arguments that you can pass to the Payment Service server with a purchase request.
Handling exceptions
The Payment Service API provides a number of exceptions that help give you fine-grained control over how your application handles errors that might occur during the in-app purchase process. You should make sure to catch the appropriate exceptions when you invoke the purchase() and getExistingPurchases() methods. Each method can throw a number of different exceptions, but you might not need to catch all of them unless you want to process each exception differently. The UserCancelException class, PaymentServerException class, DigitalGoodNotFoundException class, and IllegalApplicationException class are all subclasses of the PaymentException class, so if you configure your application to catch only PaymentException, you can process all the exceptions the same way.
There are cases where you will likely want to handle exceptions differently. For example, if a BlackBerry® device user cancels a purchase, you might want to display a particular message, or if a purchase fails because of a problem with the Payment Service, you might want to retry the purchase. Each exception has a human-readable error message that you can display by invoking getMessage() on the exception.
For descriptions of all the exceptions that can occur, see the API Reference for the Payment Service SDK at www.blackberry.com/developers/docs/payment/1.0api.
Create an application that features in-app purchases
The following task demonstrates how to create an application that displays a purchase option if the BlackBerry App World™ storefront 2.1 is installed on the BlackBerry® device (a requirement for in-app purchases). The application permits the BlackBerry device user to provide a SKU and name for digital goods. When the user clicks the Buy button, the application initiates a purchase request using the provided SKU and name. When the user clicks the Display Purchases button, the application displays a record of the past purchases.
Code sample: Creating an application that features in-app purchasing
import net.rimlib.blackberry.api.payment.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
public class PurchaseDemo extends UiApplication
{
public static void main(String[] args)
{
PurchaseDemo app = new PurchaseDemo();
app.enterEventDispatcher();
}
public PurchaseDemo()
{
pushScreen(new PurchaseDemoScreen());
}
private static class PurchaseDemoScreen extends MainScreen implements FieldChangeListener
{
private ButtonField buyButton;
private ButtonField displayButton;
private BasicEditField digitalGoodName;
private BasicEditField digitalGoodSku;
private PaymentEngine engine = PaymentEngine.getInstance();
public PurchaseDemoScreen()
{
setTitle("Payment Service SDK Demo");
if (engine != null)
{
engine.setConnectionMode(PaymentEngine.CONNECTION_MODE_LOCAL);
digitalGoodName = new BasicEditField("Digital Good Name: ",
"Sample Good");
add(digitalGoodName);
digitalGoodSku = new BasicEditField("Digital Good SKU: ",
"abc123");
add(digitalGoodSku);
HorizontalFieldManager hfm = new HorizontalFieldManager();
add(hfm);
buyButton = new ButtonField("Buy");
buyButton.setChangeListener(this);
hfm.add(buyButton);
displayButton = new ButtonField("Display Purchases");
displayButton.setChangeListener(this);
hfm.add(displayButton);
}
else
{
RichTextField errorMessage = new RichTextField("Sorry, in-app purchases
are unavailable");
add(errorMessage);
}
}
public void fieldChanged(Field field, int context)
{
if (field == buyButton)
{
String name = digitalGoodName.getText();
String sku = digitalGoodSku.getText();
PurchaseArgumentsBuilder arguments = new PurchaseArgumentsBuilder()
.withDigitalGoodSku( sku )
.withDigitalGoodName( name )
.withMetadata( name )
.withPurchasingAppName( "Payment Service SDK Demo" );
try
{
Purchase purchase = engine.purchase(arguments.build());
Dialog.inform("Purchase of " + purchase.getMetadata()
+ " is successful.");
}
catch (IllegalArgumentException e)
{
Dialog.inform(e.getMessage());
}
catch (PaymentException e)
{
Dialog.inform(e.getMessage());
}
}
else if (field == displayButton)
{
try
{
Purchase[] purchases = engine.getExistingPurchases(true);
if (purchases.length != 0)
{
if (getFieldCount() > 3)
{
deleteRange(3, (getFieldCount()-3));
}
for (int i = 0; i < purchases.length; i++ )
{
RichTextField purchaseRecord = new RichTextField("Name: "
+ purchases[i].getMetadata() + " SKU: "
+ purchases[i].getDigitalGoodSku());
add(purchaseRecord);
}
}
else
{
Dialog.inform("No existing purchases");
}
}
catch (PaymentException e)
{
Dialog.inform(e.getMessage());
}
}
}
}
}