Java (Desktop)

Requirements

Commerce SDK for Windows is supported on most x86/x64 environments capable of running the Oracle Java Virtual Machine (JVM).  Integrating a card reader and/or a receipt printer solely requires available USB ports or TCP/IP Ethernet for connectivity.  Being an SDK, Commerce processes and functionality (i.e., card reader connectivity, printer connectivity, internet connectivity) all run within the integration application (e.g., a POS system).  As such, the following system requirements apply for the application or framework in which Commerce will be integrated:

  • Windows environment supporting Oracle JVM 1.7 (32-bit ONLY)
  • Internet access (for connectivity to the payment gateway)
  • USB Port access (at least one available port for a card reader and/or receipt printer) or TCP/IP Ethernet access for card reader

In terms of integrating Commerce SDK into an existing application or framework, more information will be provided below.

Installing the Card Reader and Printer Drivers

Before you can use the card reader and printer, you’ll need to install some drivers. There are two packages. One is IngenicoUSBDrivers_2.60_setup.exe. Run that and take all the defaults. That will install the card reader drivers.

Next unzip StarMicronicsUsbDrivers.zip and run the approporiate dp_instfile appropriate to the flavor of windows you are running. This will install the printer drivers.

Building the Sample Application

A sample application package is provided(commerce-windows-sampleapp-x.y.z-release.zip), which includes the following:

  • Java source code for sample application (used to compiled the sample application and use as a fully functioning reference)
  • Pre-compiled Commerce SDK libraries (and required 3rd party dependencies)
  • Pre-compiled sample application executable
  • Developer documentation

The development environment required to build the Commerce SDK Windows Sample Application (as well as develop with Commerce itself) is fairly standard and typically includes the following:

  • Java JDK (1.7 or higher / 32-bit ONLY - but CSDK binaries are compiled for java 7, so make sure to compile for java 7)
  • IDE (IntelliJ works nicely as Commerce includes Gradle scripts, but that is a developer choice)
  • Gradle 2.13 (although later versions may be usable, testing has only been performed using version 2.13.  You are free to use any other build tool desired)

Note: For your convenience, the desktop sample app has been pre-compiled and included in the Getting Started package. Reference the file commerce-sampleapp.bat for more information on how to run the pre-compiled sample application JAR.

The Commerce SDK sample applications package contains the following directories of interest:

  • libs:  Core Commerce SDK libraries and required 3rd party libraries (all JAR files located here - with the exception of desktop-gui-0.1.0.jar - should be considered requirements for)
  • nativeLibs : Native libraries for card reader communication, printer communication, and lower-level Commerce functionality
  • src : Source code for Commerce Sample Application (a fully functional set of source code and functionality, demonstrating all available Commerce functionality)

Note : To preconfigure the sample app to load your Converge credentials, modify the desktop.properties file. These credentials can also be modified from within the sample app by clicking on Settings->Credentials

Import and Build Sample Application Project (Gradle project)
  1. Extract the sample application project to a directory of your choosing.
  2. Use the file build.gradle included with the sample application to import the sample application project into your IDE.
  3. The following information may be needed to compile/build and run the sample application:

    • Main Class : com.elavon.sample.desktopgui.CommerceSampleApplication
    • Virtual Machine Options :

      • -Djava.library.path=.\nativeLibs
      • -classpath “.\libs\*”;.
    • Virtual Machine : Reminder, you must be running a 32-bit JVM (e.g., Oracle JDK 1.7 32-bit)

The sample application should build and run using the above configuration.  Feel free to browse the sample application source code to learn more on how the Commerce SDK is used to perform transactions and accept payments.

Integrating Commerce SDK Into a Windows Application

Using the steps below, Commerce SDK may be integrated into a new or existing application for the purpose of accepting payments.

Commerce SDK and 3rd Party Code Dependencies

The following Commerce SDK libraries are provided pre-compiled for application integration (located in the /libs directory of the Getting Started package):

  • commerce-core-obfuscated-release.jar
  • commerce-datatypes-java-obfuscated-release
  • commerce-converge-obfuscated-release.jar
  • commerce-desktop-converge-obfuscated-release.jar

In addition to the core libraries mentioned above, there are some 3rd party dependencies that are required. If you plan on using the provided gradle build file, they are pulled in automatically.

Referencing and Initializing Commerce SDK

Prior to accepting payments and taking full advantage of Commerce functionality, a few steps should must be performed and items considered when coding.

1. Initialize Library and Gateway Account  

In addition to loading the Commerce libraries, a payment gateway account (e.g., Converge account) should be provided.  Code to initialize and load the account information may look like the following:

// Initialize Core Commerce SDK
ECCError error = ECLCommerceDesktop.initialize();
if (error != null)
{
    return error;  // Ensure no errors when initializing Commerce SDK
}

2. Implement the ECLConvergeAccountListener interface (See class CommerceAccountListener in the sample application)

The ECLConvergeAccountListener interface defines the methods where specific properties can be overridden for a gateway account.

import com.elavon.commerce.ECCSensitiveData;
import com.elavon.commerce.ECLAccountInterface;
import com.elavon.commerce.ECLConvergeAccountListener;
import com.elavon.commerce.ECLPersistentSettingsInterface;
import com.elavon.commerce.ECLProxyInfo;
import com.elavon.commerce.ECLTransactionProgress;
import com.elavon.commerce.ECLUpdateKeysListener;
import com.elavon.commerce.common.ECCError;
import com.elavon.commerce.common.ECLServerType;
import com.elavon.commerce.datatype.ECLCurrencyCode;
import com.elavon.commerce.datatype.ECLLanguageInformation;
import deckard.content.Context;
import deckard.content.desktop.DesktopContext;

import java.util.logging.Logger;

import static com.elavon.sample.desktopgui.CommerceSettings.MERCHANT_ID;
import static com.elavon.sample.desktopgui.CommerceSettings.PIN;
import static com.elavon.sample.desktopgui.CommerceSettings.SERVER_TYPE;
import static com.elavon.sample.desktopgui.CommerceSettings.USER_ID;
import static com.elavon.sample.desktopgui.CommerceSettings.VENDOR_APP_NAME;
import static com.elavon.sample.desktopgui.CommerceSettings.VENDOR_APP_VERSION;
import static com.elavon.sample.desktopgui.CommerceSettings.VENDOR_ID;

public class CommerceAccountListener implements ECLConvergeAccountListener
{
    private final Logger logger = Log.getLogger();

    private CommerceAccount commerceAccount;
    private CommerceSettings commerceSettings;

    CommerceAccountListener(CommerceAccount commerceAccount, CommerceSettings commerceSettings)
    {
        this.commerceAccount = commerceAccount;
        this.commerceSettings = commerceSettings;
    }

    @Override
    public Context getApplicationContext()
    {
        // logger.info("AccountListener getApplicationContext");
        return new DesktopContext();

    }

    @Override
    public void accountDidInitialize(ECLAccountInterface eclAccountInterface)
    {
        commerceAccount.setEclAccountInterface(eclAccountInterface);
        logger.info("AccountListener accountDidInitialize");

        // get notifications when a key update occurs
        eclAccountInterface.setUpdateKeysListener(new ECLUpdateKeysListener()
        {
            @Override
            public void updateKeysFailed(ECCError eccError)
            {
                logger.info("updateKeysFailed " + eccError.getDebugDescription());
            }

            @Override
            public void updateKeysCompleted()
            {
                // we let the progress message update the UI
            }

            @Override
            public void updateKeysProgress(ECLTransactionProgress eclTransactionProgress)
            {
                logger.info("cardReaderProgress " + eclTransactionProgress.toString());
            }
        });

        // optionally, set the terminal default language
        String languageAsString = commerceSettings.getProperty(CommerceSettings.TERMINAL_CONFIGURATION_LANGUAGE_CODE);
        if (languageAsString != null)
        {
            ECLLanguageInformation language = ECLLanguageInformation.getLanguageFromString(languageAsString);
            eclAccountInterface.overrideDefaultTerminalLanguage(language);
        }
    }

    @Override
    public void accountDidFailToInitialize(ECLAccountInterface eclAccountInterface, ECCError eccError)
    {
        logger.warning("AccountListener accountDidFailToInitialize");
    }

    @Override
    public void accountDefaultCurrencyDidChange(ECLAccountInterface eclAccountInterface, ECLCurrencyCode currencyCode)
    {
        logger.info("AccountListener accountDefaultCurrencyDidChange " + currencyCode.toString());
        commerceAccount.setCurrency(currencyCode);
    }

    @Override
    public ECLServerType getServerType()
    {
        //currently only Demo or Production environment is accessible to integrator.
        return ECLServerType.DEMO // ECLServerType.PROD;
    }

    @Override
    public ECCSensitiveData getMerchantId(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(MERCHANT_ID));
    }

    @Override
    public ECCSensitiveData getUserId(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(USER_ID));
    }

    @Override
    public ECCSensitiveData getPin(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(PIN));
    }

    @Override
    public ECCSensitiveData getVendorId(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(VENDOR_ID));
    }

    @Override
    public ECCSensitiveData getVendorAppName(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(VENDOR_APP_NAME));
    }

    @Override
    public ECCSensitiveData getVendorAppVersion(ECLAccountInterface eclAccountInterface)
    {
        return new ECCSensitiveData(commerceSettings.getProperty(VENDOR_APP_VERSION));
    }

    @Override 
    public ECLProxyInfo getProxyInfo()
    {
        return null;
    }

    //currently not applicable for desktop
    @Override 
    public ECLPersistentSettingsInterface getPersistentSettings()
    {
       return null;
    }
}

3. Create Gateway Account (See class CommerceAccount in the sample application)

// Create a Gateway Account Listener Implementation (See Converge account implementation CommerceAccountListener in the sample application)
// For more information on how to specify account information, as well as using the test environment, see class CommerceSettings in the sample application)
CommerceAccountListener commerceConvergeAccountListener = new CommerceAccountListener(this, commerceSettings);

// Load the Account Information into the Commerce SDK Instance
ECLCommerce.createAccount(commerceConvergeAccountListener, new ECLDispatcher(new DesktopHandler()));

At this point, barring any errors, Commerce SDK and the gateway account is initialized, ready to being processing transactions.

4. Setup a Simple Sale/Auth Transaction (See class  Transaction  in the sample application )

Various options, such as gratuity-on-the-PIN Pad, tax, partial approval, pre-auth, and more are available when performing a sale/auth transaction (and included as examples).  For the purpose of this example, a basic sale/auth is setup to process as a card (i.e. credit/debit) transaction.

// Define the base Transaction AMOUNT (using Currency Code and Amount in minor units.
// For this example, the amount $19.95 is used.
ECLMoney baseTransactionAmount = new ECLMoney(ECLCurrencyCode.USD, 1995);

// Retrieve an Instance of ECLCardTenderInterface (implementation used for performing card-based operations)
ECLCardTenderInterface cardTenderInterface = yourInstanceOfECLAccountInterface.getTransactionProcessor().createCardTender();

// Create an Instance of ECLCurrencyTransactionInterface (implementation used for card-based operations where money is changing hands)
// For reference a non-currency transaction is similar to a balance inquiry or loyalty program operation.
ECLCurrencyTransactionInterface currencyTransactionInterface = yourInstanceOfECLAccountInterface.getTransactionProcessor().createSaleTransactionWithSubtotal(baseTransactionAmount);

Performing the above steps defines the base set of information needed to process a transaction.  Using the above information, transaction processing can start.

5. Perform a Card Read and Process the Simple Sale/Auth Transaction (See class  Transaction  in the sample application )

Where the rubber truly meets the road!  With the transaction information defined, the card reader can be engaged and card data collected for the purpose of taking payment!

// Create an Instance of ECLTransactionProcessingListener (handle all callbacks and any requests for information during transaction processing)
ECLTransactionProcessingListener transactionListener = new ECLTransactionProcessingListener()
{
    @Override
    public void uponSearchingDevice(ECLConnectionMethod eclConnectionMethod)
    {
        // do something - look in the sample app code for a concrete example
    }

    @Override
    public void transactionDidComplete(ECLTransactionInterface transaction, ECLTenderInterface tender,
        final ECLTransactionOutcome outcome)
    {
        // do something - look in the sample app code for a concrete example
    }

    @Override
    public void transactionDidCancel(ECLTransactionInterface transaction, ECLTenderInterface tender)
    {
        // do something - look in the sample app code for a concrete example
    }

    @Override
    public void transactionDidFail(ECLTransactionInterface transaction, ECLTenderInterface tender, List<ECCError> errors)
    {
        // do something - look in the sample app code for a concrete example
    }

    @Override
    public void shouldProvideInformation(final ECLTransactionInterface transaction, final ECLTenderInterface tender,
        final ECLTransactionRequirementsInterface transactionRequires, final ECLTenderRequirementsInterface tenderRequires)
    {
        // do something - look in the sample app code for a concrete example
    }

    @Override
    public void shouldSetCardReaderToUse(ECLTransactionInterface transaction, ECLTenderInterface tender,
        List<String> cardReadersReadyForUse)
    {
        // do something - look in the sample app code for a concrete example
    }

    public void transactionProgress(final ECLTransactionProgress progress, final ECLTransactionInterface transaction,
        final ECLTenderInterface tender)
    {
        // do something - look in the sample app code for a concrete example
    }
};

// Initiate Transaction Processing!
yourInstanceOfECLAccountInterface.getTransactionProcessor().processTransaction(currencyTransactionInterface, cardTenderInterface, transactionListener);