FileDocCategorySizeDatePackage
PaymentModule.javaAPI DocphoneME MR2 API (J2ME)72607Wed May 02 18:00:44 BST 2007com.sun.j2me.payment

PaymentModule

public abstract class PaymentModule extends Object implements TransactionProcessor
This class represents a payment module. An instance of this class accepts payment requests from transaction modules. For each payment request it creates an instance of the Transaction class, set itself as an initial transaction processor of this transaction and adds it to the internal queue.

The internal queue is processed by a background thread created by the instance of the payment module. This thread iterates through the queued transactions and calls their process methods. This changes the internal states of the transactions until they are fully processed and are removed from the queue.

The process method of a transaction delegates the call to the associated transaction processor. This is initially the payment module. That allows him to show a provider selection form. After the user selects the provider for the transaction, the payment module changes the transaction processor of the transaction to the provider's payment adapter. This adapter gets the control over the transaction till it finishes the transaction and returns the control over it back to the payment module. The payment module finally removes the transaction from the transaction queue and notifies the application about the result of the payment.

The notification is done by creating a new transaction record for the finished transaction and adding it to the transaction notification queue which is processed by an extra thread which handles the notifications. Each transaction record is automatically added to the transaction store, so it can be obtained after crashing of the emulator.

version

Fields Summary
private static SecurityToken
classSecurityToken
This class has a different security domain than the MIDlet suite.
private static final int
ALERT_TIMEOUT
Standard timeout for alerts.
private static final String
PAYMENT_MODULE
The property that contains payment module class name.
private static PaymentModule
paymentModule
Payment module singleton.
private TransactionProcessingThread
processingThread
Transaction processing thread.
private TransactionNotificationThread
notificationThread
Transaction state notification thread.
private final Utils
utilities
Pointer to utility methods class.
private final String
NEVER
'NEVER' string
private Hashtable
paymentAdapters
Array of created payment adapters.
private PaymentModuleUI
currentUI
Current payment module UI.
private static final int
MAX_RETRY_COUNT
Number of payment update file download attempts.
private static final int
RETRY_SHIFT
UI update constant.
private static final int
STATE_CONNECTING
Payment info update stage.
private static final int
STATE_SENDING_REQUEST
Payment info update stage.
private static final int
STATE_RETRY_WAITING
Payment info update stage.
private static final int
STATE_DOWNLOADING
Payment info update stage.
private static final int
STATE_VERIFYING
Payment info update stage.
private static final int
STATE_FINISHED
Payment info update stage.
private static final int
MIME_TYPE
Index of MIME type object inside content type array.
private static final int
CHARSET
Index of CHARSET type object inside content type array.
private static final int
TRANSFER_CHUNK
Maximum size of payment update file.
private static final String
UPDATE_MIME_TYPE
Payment info update file MIME type.
private static final char[]
CHARSET_MASK
Mask for content type searching.
Constructors Summary
protected PaymentModule()
Creates a new instance of PaymentModule.

    
               
      
    
Methods Summary
public final synchronized intaddTransaction(TransactionModuleImpl transactionModule, int featureID, java.lang.String featureTitle, java.lang.String featureDescription, byte[] payload)
Creates a new transaction from the given payment requests. It sets the payment module as a transaction processor and adds the new transaction to the transaction queue. Returns a generated identification number, which identifies the new transaction.

param
transactionModule the transaction module, which called the method
param
featureID the identifier of the feature to be paid for
param
featureTitle the title of the feature
param
featureDescription the description of the feature
param
payload the payload to be transfered as a part of the payment or null if no such payload required
return
the identification number of the transaction
throws
TransactionModuleException if there is no more space to store the new transaction

        
        // execute the transaction processing thread if not running
        if (processingThread == null) {
            processingThread = new TransactionProcessingThread();
            processingThread.start();
        }
        
        TransactionStore transactionStore = getTransactionStore();
        
        Transaction transaction = new Transaction(this, transactionModule,
                featureID, featureTitle, featureDescription, payload);
        
        int transactionID;
        try {
            transactionID = transactionStore.reserve(
                    transactionModule.getApplicationID(), transaction);
        } catch (IOException e) {
            throw new TransactionModuleException("No more space for " +
                    "transaction records");
        }
        transaction.setTransactionID(transactionID);
        
        PaymentInfo paymentInfo = getPaymentInfo(transaction);
        synchronized (paymentInfo) {
            processingThread.addTransaction(transaction);
            
            if (paymentInfo.needsUpdate()) {
                try {
                    paymentInfo.wait();
                } catch (InterruptedException e) {
                    // ignore
                }
                
                if (paymentInfo.needsUpdate()) {
                    throw new TransactionModuleException("The provisioning " +
                            "information needs an update");
                }
            }
        }
        
        return transactionID;
    
final voidaddTransactionForNotification(javax.microedition.payment.TransactionRecord record, TransactionModuleImpl module)
Adds the given transaction record to the transaction notification queue and associates it with the given payment module. The payment module holds a reference to the listener, which should be notified.

param
record the transaction record
param
module the transaction module

        // execute the transaction notification thread if not running
        if (notificationThread == null) {
            notificationThread = new TransactionNotificationThread();
            notificationThread.start();
        }
        
        notificationThread.addTransaction(record, module);
    
final voidaddTransactionsForNotification(javax.microedition.payment.TransactionRecord[] records, TransactionModuleImpl module)
Adds the given transaction records to the transaction notification queue and associates them with the given payment module. The payment module holds a reference to the listener, which should be notified.

param
records an array of the transaction records
param
module the transaction module

        // execute the transaction notification thread if not running
        if (notificationThread == null) {
            notificationThread = new TransactionNotificationThread();
            notificationThread.start();
        }
        
        notificationThread.addTransactions(records, module);
    
protected voidassignTransaction(Transaction transaction, int providerID)
Assigns the given transaction to the provider identified by its identification number. It sets the transaction processor of the transaction to the provider's payment adapter.

param
transaction the transaction
param
providerID the provider id

        PaymentInfo paymentInfo = getPaymentInfo(transaction);
        int priceTag = paymentInfo.getPriceTagForFeature(
                transaction.getFeatureID());
        
        ProviderInfo providerInfo = paymentInfo.getProvider(providerID);
        // get the adapter instance for the given provider
        PaymentAdapter adapter = null;
        try {
            adapter = getAdapter(providerInfo.getAdapter(),
                    providerInfo.getConfiguration());
        } catch (PaymentException e) {
        }
        
        // fill the transaction fields with the provider specific values
        transaction.setProviderName(providerInfo.getName());
        transaction.setCurrency(providerInfo.getCurrency());
        transaction.setPrice(providerInfo.getPrice(priceTag));
        transaction.setSpecificPriceInfo(
                providerInfo.getPaySpecificPriceInfo(priceTag));
        
        // === DEBUG MODE ===
        // let a subclass to handle the transaction in the debug mode, if it
        // does, don't forward the control over the transaction to the payment 
        // adapter
        if (handleTransactionDebugMode(transaction)) {
            return;
        }
        // === DEBUG MODE ===
        
        // set the adapter to be a transaction processor for the transaction
        transaction.setTransactionProcessor(adapter);
        
        // update the state of the transaction
        transaction.setState(Transaction.ASSIGNED);
    
final voidcontinueProcessing()
Signals the transaction processing thread to continue processing of the queued transactions. It means that there is at least one transaction in the queue, which is not waiting for some event (an user action, finishing of some payment adapter's thread, etc.).

        processingThread.continueWork();
    
protected PaymentAdaptercreateAdapter(java.lang.String adapter, java.lang.String configuration)
Creates the payment adapter for the given registered adapter name and the adapter configuration string. It returns null if no such adapter can be created.

param
adapter the registered adapter name
param
configuration the adapter configuration string
return
the instance of the payment adapter or null
throws
PaymentException if the adapter configuration string has an invalid format

        if ("PPSMS".equals(adapter)) {
            if (configuration.indexOf(',") != -1) {
                String mcc =
                        configuration.substring(0, configuration.indexOf(',"));
                String mnc =
                        configuration.substring(configuration.indexOf(',") + 1);
                
                if ((mcc != null) && (mnc != null)) {
                    mcc = mcc.trim();
                    mnc = mnc.trim();
                    
                    try {
                        Integer.parseInt(mcc);
                        Integer.parseInt(mnc);
                    } catch (NumberFormatException nfe) {
                        throw new PaymentException(
                            PaymentException.INVALID_ADAPTER_CONFIGURATION, 
                            configuration);
                    }
                    
                    if ((mcc.length() != 3) || (mnc.length() < 2) || 
                            (mnc.length() > 3)) {
                        throw new PaymentException(
                            PaymentException.INVALID_ADAPTER_CONFIGURATION, 
                            configuration);
                    }
                    
                    // get system property
                    String MCC = System.getProperty("MCC");
                    // get jsr default property
                    if (null == MCC) {
                        MCC = System.getProperty("payment.mcc");
                    }
                    // get system property
                    String MNC = System.getProperty("MNC");
                    // get jsr default property
                    if (null == MNC) {
                        MNC = System.getProperty("payment.mnc");
                    }

                    if (mcc.equals(MCC) &&
                            mnc.equals(MNC)) {
                        return PPSMSAdapter.getInstance(configuration);
                    } else {
                        return null;
                    }
                } else {
                    throw new PaymentException(
                            PaymentException.INVALID_ADAPTER_CONFIGURATION,
                            configuration);
                }
            } else {
                throw new PaymentException(
                            PaymentException.INVALID_ADAPTER_CONFIGURATION,
                            configuration);
            }
        }
        return null;
    
private javax.microedition.io.HttpConnectioncreateConnection(java.lang.String url)
Creates a http or https connection with the payment update server. After opening the connection it sends the http request for the update file and checks the reply. If everything is correct it returns the connection which can be used to get the update file.

param
url the URL of the payment update file
return
the opened connection
throws
PaymentException indicates failure
throws
IOException indicates failure
throws
InterruptedException indicates that the thread has been interrupted while waiting for the server

        HttpConnection httpConnection = null;
        int responseCode = -1;
        
        try {
            int retry = 0;
            do {
                currentUI.notifyStateChange(STATE_CONNECTING +
                        (retry << RETRY_SHIFT));
                
                Connection connection;
                try {
                    connection = Connector.open(url);
                } catch (IllegalArgumentException e) {
                    throw new PaymentException(
                            PaymentException.INVALID_UPDATE_URL, url, null);
                } catch (ConnectionNotFoundException e) {
                    throw new PaymentException(
                            PaymentException.INVALID_UPDATE_URL, url, null);
                }
                
                if (!(connection instanceof HttpConnection)) {
                    connection.close();
                    throw new PaymentException(
                            PaymentException.INVALID_UPDATE_URL, url, null);
                }
                
                httpConnection = (HttpConnection)connection;
                
                // set User-Agent
                String prof = System.getProperty("microedition.profiles");
                int space = prof.indexOf(' ");
                if (space != -1) {
                    prof = prof.substring(0, space);
                }
                httpConnection.setRequestProperty("User-Agent", "Profile/" +
                        prof + " Configuration/" +
                        System.getProperty("microedition.configuration"));
                
                // set Accept-Charset
                httpConnection.setRequestProperty("Accept-Charset", "UTF-8, " +
                        System.getProperty("microedition.encoding"));
                
                // set Accept-Language
                String locale = System.getProperty("microedition.locale");
                if (locale != null) {
                    httpConnection.setRequestProperty("Accept-Language",
                            locale);
                }
                
                currentUI.notifyStateChange(STATE_SENDING_REQUEST +
                        (retry << RETRY_SHIFT));
                
                try {
                    responseCode = httpConnection.getResponseCode();
                } catch (IOException e) {
                    if (httpConnection.getHost() == null) {
                        throw new PaymentException(
                                PaymentException.INVALID_UPDATE_URL, url, null);
                    }
                    
                    throw new PaymentException(
                            PaymentException.UPDATE_SERVER_NOT_FOUND,
                            url, null);
                }
                
                if ((responseCode != HttpConnection.HTTP_UNAVAILABLE) ||
                        (++retry > MAX_RETRY_COUNT)) {
                    break;
                }
                
                long sleepTime = 10000;
                
                String value = httpConnection.getHeaderField("Retry-After");
                // parse the Retry-After field
                if (value != null) {
                    try {
                        sleepTime = Integer.parseInt(value) * 1000;
                    } catch (NumberFormatException ne) {
                        // not a number
                        try {
                            sleepTime = DateParser.parse(value);
                            sleepTime -= System.currentTimeMillis();
                        } catch (IllegalArgumentException de) {
                        }
                    }
                }
                
                httpConnection.close();
                httpConnection = null;
                
                if (sleepTime < 0) {
                    sleepTime = 10000;
                } else if (sleepTime > 60000) {
                    sleepTime = 60000;
                }
                
                currentUI.notifyStateChange(STATE_RETRY_WAITING +
                        (retry << RETRY_SHIFT));
                
                Thread.sleep(sleepTime);
                
            } while (true);
            
            switch (responseCode) {
                case HttpConnection.HTTP_OK:
                    break;
                case HttpConnection.HTTP_NOT_FOUND:
                    throw new PaymentException(
                            PaymentException.UPDATE_NOT_FOUND, url, null);
                case HttpConnection.HTTP_UNAVAILABLE:
                    throw new PaymentException(
                            PaymentException.UPDATE_SERVER_BUSY, url, null);
                default:
                    throw new PaymentException(
                            PaymentException.UPDATE_REQUEST_ERROR,
                            Integer.toString(responseCode), null);
            }
            
        } catch (PaymentException e) {
            if (httpConnection != null) {
                httpConnection.close();
            }
            // rethrow
            throw e;
        } catch (IOException e) {
            if (httpConnection != null) {
                httpConnection.close();
            }
            // rethrow
            throw e;
        }
        
        return httpConnection;
    
public abstract TransactionModuleImplcreateTransactionModule(java.lang.Object object)
It's a factory method for TransactionModuleImpl.

param
object the application MIDlet initiating a payment transaction
return
a new instance of a TransactionModuleImpl subclass.
throws
TransactionModuleException indicates a creation failure

private static intfindEndOf(java.lang.String string, char[] mask)
Returns the end index of the first substring of the given string which matches the given mask or -1 if no such substring can be found. The mask is given as a character array and it has only one special character '+', which represents 0..* number of space characters.

param
string the string
param
mask the mask
return
the index or -1

        char[] data = string.toCharArray();
        
        int i = 0; // index into data
        int j = 0; // index into mask
        int k = 0; // saved index
        
        while ((i < data.length) && (j < mask.length)) {
            if (mask[j] == '+") {
                if (data[i] <= ' ") {
                    ++i;
                } else {
                    ++j;
                }
                continue;
            }
            if (data[i] == mask[j]) {
                ++i;
                ++j;
                continue;
            }
            i = k + 1;
            j = 0;
            k = i;
        }
        
        for (; (j < mask.length) && (mask[j] == '+"); ++j) {
        }
        
        return (j == mask.length) ? i : -1;
    
PaymentAdaptergetAdapter(java.lang.String name, java.lang.String configuration)
Returns the payment adapter for the given registered adapter name and the adapter configuration string. It either returns an adapter created before for the given combination of name and providerString or creates a new instance if the old one doesn't exist. It returns null if no adapter of the given parameters can be created.

param
name the registered adapter name
param
configuration the adapter configuration string
return
the instance of the payment adapter or null
throws
PaymentException if the adapter configuration string has an invalid format

        String adapterLookupString = name + "#" +
                normalizeConfigurationString(configuration);
        PaymentAdapter adapter = (PaymentAdapter)paymentAdapters.get(
                adapterLookupString);
        if (adapter == null) {
            adapter = createAdapter(name, configuration);
            if (adapter != null) {
                paymentAdapters.put(adapterLookupString, adapter);
            }
        }
        return adapter;
    
private java.lang.StringgetErrorMessage(java.lang.Exception e)
Returns an error message for the given exception. It handles exceptions thrown during a payment update.

param
e the exception
return
the error message

    
                                 
        
        int prefixKey = Utils.PAYMENT_ERROR_PREFIX;
        int suffixKey = Utils.PAYMENT_ERROR_SUFFIX;
        int key = -1;
        if (e instanceof SecurityException) {
            key = Utils.PAYMENT_ERROR_PERMISSIONS;
            suffixKey = -1;
        } else if (e instanceof IOException) {
            key = Utils.PAYMENT_ERROR_DOWNLOAD_FAILED;
            suffixKey = -1;
        } else if (e instanceof PaymentException) {
            PaymentException pe = (PaymentException)e;
            
            switch (pe.getReason()) {
                case PaymentException.UNSUPPORTED_PAYMENT_INFO:
                case PaymentException.UNSUPPORTED_ADAPTERS:
                case PaymentException.UNSUPPORTED_PROVIDERS:
                case PaymentException.UNSUPPORTED_URL_SCHEME:
                case PaymentException.UNSUPPORTED_UPDATE_CHARSET:
                    key = Utils.PAYMENT_ERROR_UPDATE_NOT_SUPPORTED;
                    break;
                case PaymentException.INFORMATION_NOT_YET_VALID:
                    key = Utils.PAYMENT_ERROR_UPDATE_NOT_YET_VALID;
                    break;
                case PaymentException.INFORMATION_EXPIRED:
                    key = Utils.PAYMENT_ERROR_UPDATE_EXPIRED;
                    break;
                case PaymentException.MISSING_MANDATORY_ATTRIBUTE:
                case PaymentException.INVALID_ATTRIBUTE_VALUE:
                case PaymentException.INVALID_ADAPTER_CONFIGURATION:
                case PaymentException.INVALID_PRICE_INFORMATION:
                case PaymentException.INVALID_PROPERTIES_FORMAT:
                    key = Utils.PAYMENT_ERROR_UPDATE_INVALID;
                    break;
                case PaymentException.INCOMPLETE_INFORMATION:
                    key = Utils.PAYMENT_ERROR_UPDATE_INCOMPLETE;
                    break;
                case PaymentException.UPDATE_SERVER_NOT_FOUND:
                case PaymentException.UPDATE_NOT_FOUND:
                case PaymentException.INVALID_UPDATE_URL:
                    key = Utils.PAYMENT_ERROR_UPDATE_NOT_FOUND;
                    break;
                case PaymentException.UPDATE_SERVER_BUSY:
                case PaymentException.UPDATE_REQUEST_ERROR:
                    key = Utils.PAYMENT_ERROR_CONNECTION_FAILED;
                    break;
                case PaymentException.INVALID_UPDATE_TYPE:
                    key = Utils.PAYMENT_ERROR_UPDATE_INVALID_TYPE;
                    break;
                case PaymentException.EXPIRED_PROVIDER_CERT:
                    key = Utils.PAYMENT_ERROR_CERTIFICATE_EXPIRED;
                    break;
                case PaymentException.INVALID_PROVIDER_CERT:
                    key = Utils.PAYMENT_ERROR_CERTIFICATE_INCORRECT;
                    break;
                case PaymentException.EXPIRED_CA_CERT:
                case PaymentException.NO_TRUSTED_CHAIN:
                    key = Utils.PAYMENT_ERROR_CERTIFICATE_UNTRUSTED;
                    break;
                case PaymentException.SIGNATURE_VERIFICATION_FAILED:
                    key = Utils.PAYMENT_ERROR_VERIFICATION_FAILED;
                    break;
            }
        }
        
        StringBuffer buffer = new StringBuffer(utilities.getString(prefixKey));
        
        if (key != -1) {
            buffer.append(" ");
            buffer.append(utilities.getString(key));
            if (suffixKey != -1) {
                buffer.append(" ");
                buffer.append(utilities.getString(suffixKey));
            }
        }
        
        return buffer.toString();
    
public static com.sun.j2me.payment.PaymentModulegetInstance()
Returns an instance of the PaymentModule class. It creates only one instance and reuses it each time the method is called.

return
the instance

        if (paymentModule == null) {
            String className = System.getProperty(PAYMENT_MODULE);
            if (className != null) {
                try {
                    paymentModule = (PaymentModule)
                    Class.forName(className).newInstance();
                } catch (ClassNotFoundException  cnfe) {
                    // intentionally ignored
                } catch (InstantiationException  ie) {
                    // intentionally ignored
                } catch (IllegalAccessException  iae) {
                    // intentionally ignored
                }
            }
//            if (paymentModule == null) {
//                paymentModule = new PaymentModule();
//            }
        }
        return paymentModule;
    
public final intgetNextApplicationID()
Returns a new application ID, which can be used to store transaction records to the transaction store.

return
the new application ID
throws
IOException if there is a I/O failure while working with the store

        return getTransactionStore().getNextApplicationID();
    
protected static PaymentInfogetPaymentInfo(Transaction transaction)
Returns the associated payment information for the given transaction.

param
transaction the transaction
return
the payment information

        return transaction.getTransactionModule().getPaymentInfo();
    
protected abstract TransactionStoregetTransactionStore()
Returns an instrance of the TransactionStore wich is used for storing of all transaction records produced in the payment module. There is only one such instance, which is returned each time the method is called. This instance is used from both internal threads of the payment module (TransactionProcessingThread and TransactionNotificationThread) and it is left to an implementation of the TransactionStore to be thread safe.

return
the transaction store

protected abstract UtilsgetUtilities()
Returns an instance of Utils subclass.

return
the instance

protected final int[]getValidProviders(PaymentInfo paymentInfo)
Returns an array of provider identifiers, which could be used to pay for features.

param
paymentInfo the payment information for the MIDlet which initiated the payment
return
the array of provider identifiers

        int numProviders = paymentInfo.getNumProviders();
        int numAccepted = 0;
        boolean[] accepted = new boolean[numProviders];
        
        for (int i = 0; i < numProviders; ++i) {
            accepted[i] = false;
            
            ProviderInfo providerInfo = paymentInfo.getProvider(i);
            
            PaymentAdapter adapter = null;
            try {
                adapter = getAdapter(providerInfo.getAdapter(),
                        providerInfo.getConfiguration());
            } catch (PaymentException e) {
            }
            if (adapter == null) {
                continue;
            }
            
            accepted[i] = true;
            ++numAccepted;
        }
        
        int[] providers = new int[numAccepted];
        for (int i = 0, j = 0; i < numProviders; ++i) {
            if (accepted[i]) {
                providers[j++] = i;
            }
        }
        
        return providers;
    
protected booleanhandleAutoRequestMode(Transaction transaction)
Handles the auto request debug mode for the given transaction. It's called from the parts of the PaymentModule code where this mode can be applied. If the auto request mode is in effect the transaction state is set accordingly and the method returns true.

param
transaction the transaction
return
true if the transaction is handled in the method (= the auto request mode is in effect)

        return false;
    
protected booleanhandleTransactionDebugMode(Transaction transaction)
Handles the success/failure/random debug mode for the given transaction. It's called from the parts of the PaymentModule code where this mode can be applied. If the debug mode is in effect the transaction state is set accordingly and the method returns true.

param
transaction the transaction
return
true if the transaction is handled in the method

        return false;
    
public booleanisSupportedAdapter(java.lang.String name)
Indicates if the given adapter is supported by the device.

param
name the name of the adapter
return
true if the given adapter is supported

    
                                  
        
        if ("PPSMS".equals(name)) {
            return true;
        }
        return false;
    
private static java.lang.StringnormalizeConfigurationString(java.lang.String configuration)
Normalizes the given configuration strings. It removes all white spaces before and after any comma in the string.

param
configuration the input string
return
the normalized string

        StringBuffer reconstructed = new StringBuffer();
        Vector elements = Util.getCommaSeparatedValues(configuration);
        
        int count = elements.size();
        if (count > 0) {
            reconstructed.append((String)elements.elementAt(0));
            for (int i = 1; i < count; ++i) {
                reconstructed.append(",");
                reconstructed.append((String)elements.elementAt(i));
            }
        }
        
        return reconstructed.toString();
    
private static voidparseContentType(java.lang.String[] values, java.lang.String contentType)
Extracts the MIME type and the character set from the given Content-Type value. It returns the extracted values in the given string array. They are stored under the MIME_TYPE and CHARSET indexes.

param
values the array for extracted values
param
contentType the Content-Type value

    
                                                     
           
        int index;
        String mimeType = null;
        String charset = "ISO-8859-1";  // the default charset
        
        // decode mimeType and charset
        if (contentType != null) {
            index = contentType.indexOf(';");
            mimeType = contentType;
            if (index != -1) {
                mimeType = contentType.substring(0, index);
                index = findEndOf(contentType, CHARSET_MASK);
                if (index != -1) {
                    int index2 = contentType.indexOf(';", index);
                    if (index2 == -1) {
                        index2 = contentType.length();
                    }
                    charset = contentType.substring(index, index2);
                    charset = charset.trim().toUpperCase();
                }
            }
            mimeType = mimeType.trim().toLowerCase();
        }
        
        values[MIME_TYPE] = mimeType;
        values[CHARSET] = charset;
    
protected abstract voidpreemptDisplay(SecurityToken token, Displayable nextDisplayable)
Replaces the current Displayable with the new one if the nextDisplayable is not null or it recovers the previous Displayable if the nextDisplayable is null.

param
token a security token, which allows preempting
param
nextDisplayable the Displayable to show or null if the recovery of the old Displayable is requested

public Transactionprocess(Transaction transaction)
A method which is responsible for processing of transactions which are not yet assigned to the provider specific adapters or they are finished and need to be removed from the transaction processing queue.

param
transaction the transaction to be processed
return
the processed transaction or null if the transaction should be removed from the transaction queue

        PaymentInfo paymentInfo;
        boolean needsUpdate;
        
        switch (transaction.getState()) {
            case Transaction.ENTERED:
                paymentInfo = getPaymentInfo(transaction);
                needsUpdate = paymentInfo.needsUpdate();
                // === DEBUG MODE ===
                if (!needsUpdate && handleAutoRequestMode(transaction)) {
                    break;
                }
                // === DEBUG MODE ===
                currentUI = new PaymentModuleUI(transaction);
                if (needsUpdate) {
                    transaction.setState(Transaction.UPDATE);
                } else {
                    transaction.setWaiting(true);
                    currentUI.showProviderSelectionForm();
                }
                break;
                
            case Transaction.UPDATE:
                // currentUI != null
                paymentInfo = getPaymentInfo(transaction);
                
                // validate the http or https connection now (before we preempt
                // the display)
                try {
                    String name = paymentInfo.getUpdateURL();
                    int permission = name.startsWith("https") ?
                        Permissions.HTTPS : Permissions.HTTP;
                    int colon = name.indexOf(':");
                    if (colon != -1) {
                        if (colon < name.length() - 1) {
                            name = name.substring(colon + 1);
                        } else {
                            name = "";
                        }
                    }
                    transaction.getTransactionModule().checkForPermission(
                            permission, name);
                } catch (InterruptedException e) {
                    // ignore, let the download fail
                }
                
                transaction.setWaiting(true);
                currentUI.showPaymentUpdateForm();

                Exception ex = null;
                try {
                    synchronized (paymentInfo) {
                        updatePaymentInfo(paymentInfo);
                    }
                    
                    if (paymentInfo.cache()) {
                        savePaymentInfo(transaction);
                    }
                    
                    preemptDisplay(classSecurityToken, null);
                    // === DEBUG MODE ===
                    if (handleAutoRequestMode(transaction)) {
                        transaction.setWaiting(false);
                        currentUI = null;
                        break;
                    }
                    // === DEBUG MODE ===
                    currentUI.showProviderSelectionForm();
                } catch (InterruptedException e) {
                    preemptDisplay(classSecurityToken, null);
                    if (paymentInfo.needsUpdate()) {
                        currentUI = null;
                        
                        // discard the transaction
                        transaction.setState(Transaction.DISCARDED);
                        transaction.setNeedsUI(false);
                        transaction.setWaiting(false);
                    } else {
                        currentUI.showProviderSelectionForm();
                    }
                } catch (PaymentException pe) {
                    ex = pe;
                } catch (IOException ioe) {
                    ex = ioe;
                }
                if (ex != null) {
                    preemptDisplay(classSecurityToken, null);
                    currentUI.displayException(utilities.getString(
                            Utils.PAYMENT_ERROR_DLG_CAPTION),
                            getErrorMessage(ex));

                    // don't release the process method if waiting for an
                    // update yet
                    break;
                }
                
                // release the process method if waiting for an update
                synchronized (paymentInfo) {
                    paymentInfo.notifyAll();
                }
                
                break;
                
            case Transaction.REJECTED:
            case Transaction.SUCCESSFUL:
            case Transaction.FAILED:
                TransactionStore transactionStore = getTransactionStore();
                
                try {
                    // create a transaction record for the transaction and add
                    // it to the transaction notification queue
                    TransactionRecord transactionRecord =
                            transactionStore.addTransaction(transaction);
                    addTransactionForNotification(transactionRecord,
                            transaction.getTransactionModule());
                } catch (IOException e) {
                }
                
                // fall through
                
            case Transaction.DISCARDED:
                return null;
        }
        
        return transaction;
    
protected static voidsavePaymentInfo(Transaction transaction)
Saves the payment information for the given transaction into storage.

param
transaction the transaction

        try {
            transaction.getTransactionModule().savePaymentInfo();
        } catch (IOException e) {
            // ignore
        }
    
private voidupdatePaymentInfo(PaymentInfo paymentInfo)
Updates the given payment information from the update URL. If an exception is thrown during the update, the payment information is not changed.

param
paymentInfo the payment information
throws
PaymentException indicates failure
throws
IOException indicates failure
throws
InterruptedException if the update has been stopped by the user

        String url = paymentInfo.getUpdateURL();
        
        // 1. CONNECT TO THE SERVER AND SEND REQUEST
        HttpConnection httpConnection = createConnection(url);
        
        String[] contentType = { null, null };
        parseContentType(contentType, httpConnection.getType());
        
        // check the mime type
        if (!UPDATE_MIME_TYPE.equals(contentType[MIME_TYPE])) {
            httpConnection.close();
            throw new PaymentException(PaymentException.INVALID_UPDATE_TYPE,
                    contentType[MIME_TYPE], null);
        }
        
        // 2. DOWNLOAD THE UPDATE FILE
        byte[] data = null;
        
        InputStream is;
        try {
            currentUI.notifyStateChange(STATE_DOWNLOADING);
            
            is = httpConnection.openDataInputStream();
            try {
                ByteArrayOutputStream os = new ByteArrayOutputStream(
                        TRANSFER_CHUNK);
                byte[] buffer = new byte[TRANSFER_CHUNK];
                int read;
                while ((read = is.read(buffer, 0, TRANSFER_CHUNK)) > 0) {
                    os.write(buffer, 0, read);
                }
                
                // not necessary
                os.flush();
                
                data = os.toByteArray();
                os.close();
                
            } finally {
                is.close();
            }
        } finally {
            httpConnection.close();
        }
        
        // 3. VERIFY AND UPDATE PAYMENT INFORMATION
        currentUI.notifyStateChange(STATE_VERIFYING);
        paymentInfo.updatePaymentInfo(data, contentType[CHARSET]);
        
        currentUI.notifyStateChange(STATE_FINISHED);