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 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)

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-2.4.0-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)
  • IDE (IntelliJ works nicely as Commerce includes Gradle scripts, but that is a developer choice)
  • Gradle 2.2.1 (although later versions may be usable, testing has only been performed using version 2.2.1.  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 desktop-sampleapp.bat for more information on how to run the pre-compiled sample application JAR (desktop-gui-0.1.0.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, the following 3rd party dependencies are required:

  • build-info-extractor-gradle-3.0.1.jar
  • dagger-2.0-SNAPSHOT.jar
  • deckard-core-2.4.0.jar
  • deckard-desktop-2.4.0.jar
  • ingenico-rba-icm122-java-2.4.0.jar
  • ingenico-rba-ipp320-java-2.4.0.jar
  • ingenico-rba-isc250-java-2.4.0.jar
  • ingenico-rba-obfuscated-release.jar
  • ingenico-rba-wrapper-java-proguarded-release.jar
  • jackson-annotations-2.6.5.jar
  • jackson-core-2.6.5.jar
  • jackson-databind-2.6.5.jar
  • javax.inject-1.jar
  • jsr250-api-1.0.jar
  • log4j-1.2.17.jar
  • logback-classic-1.1.2.jar
  • logback-core-1.1.2.jar
  • okhttp-2.2.0.jar
  • okio-1.0.1.jar
  • rba-desktop-2.4.0.jar
  • RBA_SDK-4.0.18.jar
  • slf4j-api-1.7.9.jar
  • stario_java_32-2.1.0.jar
  • xmlpull-1.1.3.3.jar
  • xpp3-1.1.4c.jar

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  (See class CommerceAccount in the sample application)

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
}
 
// 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 = CommerceAccountListener(this, commerceSettings, logger);
 
// 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.

2. 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.

3. 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()
{
    /**
     * Transaction Completed ...  How did it turn out?
     */
    @Override
    public void transactionDidComplete(ECLTransactionInterface transaction, ECLTenderInterface tender, final ECLTransactionOutcome outcome)
    {
        if (outcome == null)
        {
            // Warning - No outcome was present
            logger.warning("transaction completed... with null outcome");
        }
        else if (outcome.isApproved())
        {
            // Transaction was APPROVED!!!  Use the "outcome" instance for transaction details (approval code, etc.)
        }
        else
        {
            // Transaction was DECLINED!!!  Use the "outcome" instance for transaction details (approval code, etc.)
        }
 
        // Can check error field to see if an error occurred during the processing.
        // Can check signatureError field to see why signature failed to be sent
        if (outcome.isSignatureSent())
        {
            // Signature collected and sent for transaction.
        }
 
        // If the transaction happened to utilize an EMV chip card, check for any errors.
        if (outcome instanceof ECLEmvCardTransactionOutcome)
        {
            ECLEmvCardTransactionOutcome emvOutcome = (ECLEmvCardTransactionOutcome)outcome;
            // reversal error is if the transaction was approved by Converge server and card declined it but Commerce were unable to tell Converge server to reverse.
            showErrors("reversal error", Arrays.asList(emvOutcome.getReversalError()));
            // update error is if after transaction completed the card told us to update the EMV properties associated with the transaction but commerce failed to do that.
            showErrors("update error", Arrays.asList(emvOutcome.getUpdateTransactionError()));
        }
    }

    @Override
    public void transactionDidCancel(ECLTransactionInterface transaction, ECLTenderInterface tender)
    {
        // Transaction was cancelled.  Time to move on...
    }

    @Override
    public void transactionDidFail(ECLTransactionInterface transaction, ECLTenderInterface tender, List<ECCError> errors)
    {
        // Uh oh, transaction failed.  Display the errors (list).
    }

    @Override
    public void shouldProvideInformation(final ECLTransactionInterface transaction, final ECLTenderInterface tender, final ECLTransactionRequirementsInterface transactionRequires, final ECLTenderRequirementsInterface tenderRequires)
    {
        // Hold on, we need more information to process the transaction!
        // During transaction processing, this callback may be executed one or more times for the purpose
        // of collecting more information to continue processing the transaction.
 
        boolean continueTransaction = false;
        if (tenderRequires.isDigitalSignatureRequired())
        {
            // Transaction Requires a [Digital] Signature!
            // The example below simply provides a basic image block to satisfy the signature image requirement.
            // If using a smartphone, for example, capture the signature on-screen and then pass the signature image to Commerce SDK.
            // Should the signature be cancelled at this point (i.e. "cardTender.cancelSignature();"), Commerce SDK will include a blank signature line on the printed receipt.
            Thread thread = new Thread()
            {
                @Override
                public void run()
                {
                    GetSignatureDialog getSignatureDialog = new GetSignatureDialog();
                    getSignatureDialog.setLocationRelativeTo(parentFrame);
                    getSignatureDialog.setVisible(true);
                    ECLCardTenderInterface cardTender = (ECLCardTenderInterface) tender;
                    if (getSignatureDialog.isClickedOK())      /* Did the user click OK? */
                    {
                        ECLDimension minSize = tenderRequires.getMinimumDigitalSignatureSize();
                        deckard.graphics.desktop.DesktopBitmap sig = new deckard.graphics.desktop.DesktopBitmap();
                        sig.initialize(minSize.getWidth(), minSize.getHeight(), Bitmap.Config.BLACK_AND_WHITE);
                        sig.fillWithColor(0, 0, minSize.getWidth(), minSize.getHeight(), Color.BLACK);
                        cardTender.setDigitalSignature(new ECLSignatureData(sig));
                    }
                    else
                    {
                        cardTender.cancelSignature();
                    }
                    account.getTransactionProcessor().continueProcessingTransaction(transaction, tender, transactionListener);
                }
            };
            thread.start();
            return;
        }
        if (tenderRequires.getRequiresVoiceReferral() != ECLVoiceReferralRequirement.NOT_REQUIRED)
        {
            // Voice Referral Required!
            // Some transactions may require calling the issuer to obtain an approval code.
            // Should this be required, pass the approval code to Commerce SDK once the code has been provided.
            ((ECLCardTenderInterface) tender).setVoiceReferralHandledAndApproved("321");
            continueTransaction = true;
        }
        if (tenderRequires.getRequiresSignatureVerification() != ECLSignatureVerificationRequirement.NOT_REQUIRED)
        {
            // Signature Verification Required!
            // In some cases, processing may ask to verify the cardholder's signature.  Once verified, the transaction processing may continue.
            ((ECLCardTenderInterface) tender).setSignatureVerificationHandledAndVerified();
            continueTransaction = true;
        }

        if (tenderRequires.requiresSpecifyingCardPresence())
        {
            // Is the Card Physically Present at the Time of Processing?
            // When manually entering a card number, it may be necessary to indicate if the card is physically present and in-hand.
            // If the card is not present, the state should be set to "NO".
            Thread thread = new Thread()
            {
                @Override
                public void run()
                {
                    CardPresentDialog dialog = new CardPresentDialog();
                    dialog.setLocationRelativeTo(parentFrame);
                    dialog.setVisible(true);
                    if (dialog.isClickedOK())
                    {
                        if (dialog.isCardPresent())
                        {
                            ((ECLCardTenderInterface) tender).setCardPresent(ECLTriState.YES);
                        }
                        else
                        {
                            ((ECLCardTenderInterface) tender).setCardPresent(ECLTriState.NO);
                        }
                        account.getTransactionProcessor().continueProcessingTransaction(transaction, tender, transactionListener);
                    }
                    else
                    {
                        account.getTransactionProcessor().cancelTransaction(transaction, tender, transactionListener);
                    }
                }
            };
            thread.start();
            return;
        }
        if (transactionRequires.isGratuityRequired() && transaction instanceof ECLCurrencyTransactionInterface)
        {
            // Transaction Supports Gratuity...  Was a value already determined/provided?  If so, set the amount here.
            ((ECLCurrencyTransactionInterface)transaction).setGratuity(null);
            logger.info("   Gratuity");
            continueTransaction = true;
        }

        if (continueTransaction)
        {
            // Has the required information been provided?  If so, continue transaction processing.
            logger.warning("shouldProvideInformation continue");
            account.getTransactionProcessor().continueProcessingTransaction(transaction, tender, transactionListener);
        }
        else
        {
            logger.warning("shouldProvideInformation unhandled - canceling transaction");
            account.getTransactionProcessor().cancelTransaction(transaction, tender, transactionListener);
        }
    }

    @Override
    public void shouldSetCardReaderToUse(ECLTransactionInterface transaction, ECLTenderInterface tender, List<String> cardReadersReadyForUse)
    {
        // Lucky day, more than one card reader is available!
        // Should this scenario arise, provide a mechanism to select the appropriate card reader.
        logger.warning("unhandled shouldSetCardReaderToUse(List) so canceling transaction");
    }

    public void transactionProgress(final ECLTransactionProgress progress, final ECLTransactionInterface transaction, final ECLTenderInterface tender)
    {
        // Always good to know what is happening while the transaction is processing!
        // Use this callback to provide status information (i.e. "progress" messages)
        logger.info(String.format("ECLTransactionProcessingListener transactionProgress %s", progress.name()));
    }
};
 
// Initiate Transaction Processing!
yourInstanceOfECLAccountInterface.getTransactionProcessor().processTransaction(currencyTransactionInterface, cardTenderInterface, transactionListener);