Fields Summary |
---|
private static SecurityToken | classSecurityTokenThis class has a different security domain than the MIDlet suite. |
private static final int | ALERT_TIMEOUTStandard timeout for alerts. |
private static final String | PAYMENT_MODULEThe property that contains payment module class name. |
private static PaymentModule | paymentModulePayment module singleton. |
private TransactionProcessingThread | processingThreadTransaction processing thread. |
private TransactionNotificationThread | notificationThreadTransaction state notification thread. |
private final Utils | utilitiesPointer to utility methods class. |
private final String | NEVER'NEVER' string |
private Hashtable | paymentAdaptersArray of created payment adapters. |
private PaymentModuleUI | currentUICurrent payment module UI. |
private static final int | MAX_RETRY_COUNTNumber of payment update file download attempts. |
private static final int | RETRY_SHIFTUI update constant. |
private static final int | STATE_CONNECTINGPayment info update stage. |
private static final int | STATE_SENDING_REQUESTPayment info update stage. |
private static final int | STATE_RETRY_WAITINGPayment info update stage. |
private static final int | STATE_DOWNLOADINGPayment info update stage. |
private static final int | STATE_VERIFYINGPayment info update stage. |
private static final int | STATE_FINISHEDPayment info update stage. |
private static final int | MIME_TYPEIndex of MIME type object inside content type array. |
private static final int | CHARSETIndex of CHARSET type object inside content type array. |
private static final int | TRANSFER_CHUNKMaximum size of payment update file. |
private static final String | UPDATE_MIME_TYPEPayment info update file MIME type. |
private static final char[] | CHARSET_MASKMask for content type searching. |
Methods Summary |
---|
public final synchronized int | addTransaction(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.
// 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 void | addTransactionForNotification(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.
// execute the transaction notification thread if not running
if (notificationThread == null) {
notificationThread = new TransactionNotificationThread();
notificationThread.start();
}
notificationThread.addTransaction(record, module);
|
final void | addTransactionsForNotification(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.
// execute the transaction notification thread if not running
if (notificationThread == null) {
notificationThread = new TransactionNotificationThread();
notificationThread.start();
}
notificationThread.addTransactions(records, module);
|
protected void | assignTransaction(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.
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 void | continueProcessing()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 PaymentAdapter | createAdapter(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.
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.HttpConnection | createConnection(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.
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 TransactionModuleImpl | createTransactionModule(java.lang.Object object)It's a factory method for TransactionModuleImpl .
|
private static int | findEndOf(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.
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;
|
PaymentAdapter | getAdapter(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.
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.String | getErrorMessage(java.lang.Exception e)Returns an error message for the given exception. It handles exceptions
thrown during a payment update.
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.PaymentModule | getInstance()Returns an instance of the PaymentModule class. It creates
only one instance and reuses it each time the method is called.
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 int | getNextApplicationID()Returns a new application ID, which can be used to store transaction
records to the transaction store.
return getTransactionStore().getNextApplicationID();
|
protected static PaymentInfo | getPaymentInfo(Transaction transaction)Returns the associated payment information for the given transaction.
return transaction.getTransactionModule().getPaymentInfo();
|
protected abstract TransactionStore | getTransactionStore()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.
|
protected abstract Utils | getUtilities()Returns an instance of Utils subclass.
|
protected final int[] | getValidProviders(PaymentInfo paymentInfo)Returns an array of provider identifiers, which could be used to pay
for features.
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 boolean | handleAutoRequestMode(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 .
return false;
|
protected boolean | handleTransactionDebugMode(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 .
return false;
|
public boolean | isSupportedAdapter(java.lang.String name)Indicates if the given adapter is supported by the device.
if ("PPSMS".equals(name)) {
return true;
}
return false;
|
private static java.lang.String | normalizeConfigurationString(java.lang.String configuration)Normalizes the given configuration strings. It removes all white spaces
before and after any comma in the 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 void | parseContentType(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.
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 void | preemptDisplay(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 .
|
public Transaction | process(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.
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 void | savePaymentInfo(Transaction transaction)Saves the payment information for the given transaction into storage.
try {
transaction.getTransactionModule().savePaymentInfo();
} catch (IOException e) {
// ignore
}
|
private void | updatePaymentInfo(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.
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);
|