Objective-C (iOS)

In this topic:

Requirements

  • iPad and iPhone are supported.
  • Device must be running iOS 9.3 or later.
  • Device cannot be jailbroken.
  • Simulator is not supported.
  • As we are unable to build our frameworks with bitcode enabled, you will be unable to build your application with bitcode enabled.

Build the Sample Application

  1. Install Xcode version 10 or later on your Mac from the Mac App Store.

    You may be able to use an earlier version but the steps listed here are for 10 or newer.

    Refer to the Xcode support page for more information.

  2. Copy the CommerceConverge-SampleAppDebug.zip file and unzip on the command line using unzip CommerceConverge-SampleAppDebug.zip -d outputFolder, where you replace outputFolder with the folder you would like all the files to be located.

    error_outline
    important

    Do not unzip the zip file by double clicking on it as your default unzip application may cause the files to lose symbolic links that are needed inside the uncompressed folders.

    Do not release your application using the debug build of commerce frameworks that are included with the sample app or from CommerceConverge-FrameworkDebug.zip.

  3. Open Xcode and open CommerceSample.xcodeproj using the File/Open menu.

  4. Connect your iOS device to your Mac.

  5. On the toolbar where it shows what device you are building for click on it and choose your device you just connected.

  6. Now we need to change the Team and provisioning profile you are using to build the application so that it can run on phones of your team.

    • In the project navigator, click CommerceSample.
    • Under Targets, click CommerceConvergeSample. Make sure the General settings is clicked near the top of Xcode.
    • Click the team combobox and select the development team you are part of through Apple's Developer Program or you can choose your personal team (Should have your team with personal team in parenthesis).
    • You may need to click Fix Issue in order to download a code signing certificate if you have not previously. The provisioning profile is set to Automatic so Xcode should be able to create one for you when building.
  7. Press Command+B to build the sample application.

  8. Press Command+R to run the sample application.

  9. Once your application is running, you need to set the Converge credentials you want to use.

    • Click Edit Account Settings and set all the fields to your Converge merchant credentials.
    • The only externals servers available are Demo  and Prod. Set the server to the correct value for your credentials.
  10. Click Get Account Info and if everything is set up correctly, your account information will be retrieved from the server and displayed in the progress window. Or an error will show if unable to retrieve the information.

  11. If you want to run the sample app with the release build of the frameworks, run unzip CommerceConverge-FrameworkRelease.zip, and copy the frameworks using cp -a over the debug frameworks including in the sample app zip file.

    You will be unable to debug when you use the release builds of the framework.

Integrate Commerce SDK with an iOS Application

Set Up the Xcode Project to Use Commerce SDK

  1. There are 6 frameworks you need to set your application to use:

    • Commerce-Converge.framework
    • CommerceDataTypes.framework
    • CommerceStrings.framework
    • ElavonCommon.framework
    • iSMP.framework
    • RUA_MFI.framework

    Any time you copy these files from one location to another you need to use cp -a from to as the -a maintains the symbolic links in the framework. Copy all 6 frameworks into a folder under your application project location.

  2. Click your project file in the project navigator.

    Click the project name under Project.

    Click Build Settings.

    Find Framework Search Paths and double click the value to show the popup.

    Click + and add the path to where you copied the frameworks (if under the project it is best to use \$(SRCROOT + subfolderpath).

    If you want to be able to build with both release and debug frameworks, you could copy the frameworks to a release and debug folder, and set the specific folder for each by expanding Framework Search Paths and setting the correct folder for each build type.

  3. Under Build Options, make sure Enable Bitcode is set to No.

  4. Under Other Linker Flags, click + and add -ObjC.

  5. Under Target, click your target name.

    Click the Build Phases tab.

    Under Link Binary With Libraries, click +.

    In the dialog, click the Add Other button and choose the 6 frameworks.

    You also need to add libxml2, ExternalAccessory.framework, libresolv, and CoreBuetooth.framework.

  6. Under Target, click your target name.

    Click the General tab.

    Under Embedded Binaries, click +.

    If you do not see CommerceStrings.framework, click Add Other and find it that way.

  7. For iOS 9.2, Apple added some Transport Security requirements.

    In order to connect to the Converge servers, we need to override one of the requirements.

    You need to edit your PLIST file. Control click your PLIST file and choose Open As > Source Code.

    You will need to add the following NSAppTransportSecurity key to the file under the main <dict> tag:

     <key>NSAppTransportSecurity</key>
     <dict>
         <key>NSExceptionDomains</key>
         <dict>
             <key>api.convergepay.com</key>
             <dict>
                  <key>NSExceptionRequiresForwardSecrecy</key>
                  <false/>
                  <key>NSIncludesSubdomains</key>
                  <true/>
             </dict>
             <key>api.demo.convergepay.com</key>
             <dict>
                 <key>NSExceptionRequiresForwardSecrecy</key>
                 <false/>
                 <key>NSIncludesSubdomains</key>
                 <true/>
             </dict>
         </dict>
     </dict>
  8. In order to communicate via bluetooth to card readers and printers, you need to set properties in your project.

    Under Targets, click your target name.

    Click the Capabilities tab and turn on Background Modes.

    Check Uses Bluetooth LE accessories, External accessory communication and Audio, AirPlay, and Picture in Picture (The third is needed if you are using the ROAM RP457c reader).

    Also turn on Wireless Accessory Configuration if not already turned on and click Fix issues if needed.

    Open your PLIST file (most likely it is named info.plist) and click the + button next to Information Property List.

    Select Supported external accessory protocols.

    Expand the new array and add the following items based on the bluetooth card readers and printers you plan to support:

    • Ingenico card readers (iCMP, Link/2500)
      • com.ingenico.easypayemv.spm-transaction
      • com.ingenico.easypayemv.spm-configuration
      • com.ingenico.easypayemv.spm-pppchannel
    • ROAM readers (RP457c, Moby/5500)
      • com.landicorp.datapath
    • Star Micronics printers
      • jp.star-m.starpro
  9. As a security precaution, you should set custom keyboards to prevent keys from being recorded. To not allow them, you should add the following to your implementation of UIApplicationDelegate:

    -(BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(NSString *)extensionPointIdentifier {
        // Disable custom keyboards
        return NO;
    }
  10. When saving the Converge merchant credentials or any other sensitive data, do not save these to NSUserDefaults. You should save them in the keychain.

    Look in our sample application for the KeychainWrapper class that saves to the keychain.

    Also you need to have the Data Protection Entitlement switched on for your app.

    Click on the target for your app in Xcode and select the Capabilities tab. Look for Data Protection and turn the setting on.

  11. Press Command+B to make sure you can build.

Reference and Initialize Commerce SDK

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

  1. Initialize Commerce Framework

    Code to initialize and load the account information may look like the following:

    // Initialize Commerce Framework
    NSError *error = [ECLCommerce initializeCommerce];
    if (error != nil) {
        return error;  // Ensure no errors when initializing Commerce Framework
    }
  2. Implement the ECLConvergeAccountDelegate protocol.

    Code to implement could look like this:

    // In your header:
    #import <Commerce-Converge/ECLConvergeAccountDelegate.h>
    @interface ConvergeAccountDelegate : NSObject <ECLConvergeAccountDelegate>
    
    // In your .m file:
    
    @implementation ConvergeAccountDelegate
    
        + (ECCSensitiveData *)merchantId:(id<ECLAccountProtocol>)account {
        // returns your Converge account's merchant ID
    }
    
        + (ECCSensitiveData *)userId:(id<ECLAccountProtocol>)account {
        // returns your Converge account's user ID
    }
    
        + (ECCSensitiveData *)pin:(id<ECLAccountProtocol>)account {
        // returns your Converge account's PIN
    }
    
        + (ECCSensitiveData *)vendorId:(id<ECLAccountProtocol>)account {
        // returns your vendor ID
    }
    
        + (ECCSensitiveData *)vendorAppName:(id<ECLAccountProtocol>)account {
        // returns your application name
    }
    
        + (ECCSensitiveData *)vendorAppVersion:(id<ECLAccountProtocol>)account {
        // returns your application version
    }
    
        -(id<ECLPersistentSettingsProtocol>)persistentSettings {
        // implement the protocol for providing a persistent settings layer, i.e. NSUserDefaults
        // this will be called to store and retrieve various Commerce SDK settings
        // that need to persist across launches of your application
        // See sample app for implementation details
    }
    
        + (void)accountDidInitialize:(id<ECLAccountProtocol>)account {
     
        // SAVE OFF YOUR ACCOUNT TO USE
        yourInstanceOfECLAccountProtocol = account;
    
        // Once the account is initialized, 
        // You also have the chance to setup listeners for different events associated with the account
    
        // get notifications when a key update occurs
        id<ECLUpdateKeysDelegate> keysDelegate = ...;
        [account setUpdateKeysDelegate:keysDelegate];
    
        // get notifications when a scheduled reboot is about to occur - for V4 pin pads
        id<ECLCardReaderDailyRebootNotificationDelegate> rebootDelegate = ...;
        [account setCardReaderDailyRebootNotificationDelegate:rebootDelegate];

    The language used for transaction can be overridden. This applies to RBA pinpads only and will have no effect for other pinpads. This is completely optional. If not used, a possibly server supplied language preference info is going to be used. You can specify a pair of (language, country) to determine the language to be used on pinpad during a transaction. If the pair you supplied is not supported, the return value will indicate the language Commerce SDK chose to use. This is determined to be the closest match to what you specified. Currently, Commerce SDK supports English in US and English and French in Canada.

        ECLLanguageInformation * languageInfo = ...;
        [account overrideDefaultTerminalLanguage:languageInfo];

    You can override the Commerce SDK setting for the debit network preference in the US. This applies to US RBA pinpads only and will have no effect for other pinpads. This is completely optional. If not used, a possibly server supplied value is going to be used. The possible choices are: global debit network, us common debit network or no preference at all, in which case the user will be asked to do the selection on the pinpad, if applicable. Be advised that in case of contactless transactions, the pinpad will not ask the user for a selection, but it will select the AID with the highest priority remaining after filtering them based on the setting. For example, if the choice is to use the global network and there are 2 global AIDs on the card, RBA will automatically select the one with the highest priority.

        ECLDebitNetworkPreferences * debitNetworkPreferences = ...;
        [account overrideDebitNetworkPreferences:debitNetworkPreferences];
    
        // look in the sample app code for a concrete example
    }
    
        + (void)accountDidFailToInitialize:(id<ECLAccountProtocol>)account error:(NSError *)error {
        // do something - look in the sample app code for a concrete example
    }
    
        + (void)defaultCurrencyDidChange:(id<ECLAccountProtocol>)account defaultCurrencyCode:(ECLCurrencyCode)defaultCurrencyCode {
        // do something - look in the sample app code for a concrete example
    }
    
        + (ECLServerType)serverType:(id<ECLAccountProtocol>)account {
        // do something - look in the sample app code for a concrete example
    }

    Now you have an ECLConvergeAccountDelegate that you can pass to create an account in the next step.

  3. Create an account.

    Code to initialize and load the account information may look like the following:

    ConvergeAccountDelegate *convergeAccountDelegate = [[ConvergeAccountDelegate alloc] init];
    [ECLCommerce createAccount:convergeAccountDelegate queue:dispatch_get_main_queue()];

    Load the Account Information into the Commerce Framework Instance. The first parameter is the instance of an implementation of the ECLConvergeAccountDelegate. See the sample app for an example. See documentation for ECLConvergeAccountDelegate for what methods need to be implemented. The queue parameter is the dispatch_queue_t that all callbacks will be done on. If you use a queue other than the main queue you should be aware that you can only update the UI on the main queue.

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

    error_outline
    note

    You have to wait for accountDidInitialize on your ECLConvergeAccountDelegate implementation before you will have an account instance to use for transactions.

  4. Set up a simple Sale or Auth transaction.

    Refer to the TransactionViewController class 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 = [[ECLMoney alloc] initWithMinorUnits:1995 withCurrencyCode:ECLCurrencyCode_USD];
     
    // Retrieve an Instance of ECLCardTenderInterface (implementation used for performing card-based operations)
    id<ECLCardTenderProtocol> cardTenderProtocol = [yourInstanceOfECLAccountProtocol.transactionProcessor 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.
    id<ECLCurrencyTransactionProtocol> currencyTransactionProtocol = [yourInstanceOfECLAccountProtocol.transactionProcessor 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. Implement ECLTransactionProcessingDelegate.

    Refer to the MainViewController class in the sample application and the Continue Transaction for more information.

    // Create an implementation of ECLTransactionProcessingDelegate (handle all callbacks and any requests for information during transaction processing)
    
    // in your header file
    #import <Commerce-Converge/ECLTransactionProcessingDelegate.h>
    @interface TransactionProcessingDelegate : NSObject<ECLTransactionProcessingDelegate>
     
    // In your .m file
    ...
    @implementation TransactionProcessingDelegate
        + (void)transactionDidComplete:(id<ECLTransactionProtocol>)transaction using:(id<ECLTenderProtocol>)tender outcome:(ECLTransactionOutcome *)outcome {
        // do something - look in the sample app code for a concrete example
    }
        + (void)transactionDidCancel:(id<ECLTransactionProtocol>)transactionParam using:(id<ECLTenderProtocol>)tenderParam {
        // do something - look in the sample app code for a concrete example
    }
        + (void)transactionDidFail:(id<ECLTransactionProtocol>)transactionParam using:(id<ECLTenderProtocol>)tenderParam errors:(NSArray *)arrayOfNSErrors {
        // Transaction failed. Check the array of NSErrors to see why.
        // Use nsError.debugDescription to log more information
        // Look in the sample app code for a concrete example
    }
    
        + (void)shouldProvideInformation:(id<ECLTransactionProtocol>)transactionParam tender:(id<ECLTenderProtocol>)tenderParam transactionRequires:(id<ECLTransactionRequirementsProtocol>)transactionRequires tenderRequires:(id<ECLTenderRequirementsProtocol>)tenderRequires {
        // 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.
        // Look in the sample app code for a concrete example
    }
     
        + (void)shouldSetCardReaderToUse:(id<ECLTransactionProtocol>)transactionParam tender:(id<ECLTenderProtocol>)tenderParam cardReaders:(NSArray *)cardReadersReadyForUse {
        // Commerce detected more than one card reader so you need to let the user choose which one from the list should be used
        // Should this scenario arise, provide a mechanism to select the appropriate card reader.
        // Look in the sample app code for a concrete example
    }
        + (void)transactionProgress:(ECLTransactionProgress)progress transaction:(id<ECLTransactionProtocol>)transactionParam using:(id<ECLTenderProtocol>)tenderParam {
        // Always good to know what is happening while the transaction is processing!        
        // Use this callback to provide status information (i.e. "progress" messages)
        // Look in the sample app code for a concrete example
    }
  6. Now you have everything needed to process the transaction.

    ECLTransactionProcessingDelegate *transactionProcessingDelegate = [[ECLTransactionProcessingDelegate alloc] init];
     
    // Now use all the instances you have created in the previous steps to start the transaction!
    yourInstanceOfECLAccountProtocol.transactionProcessor processTransaction:currencyTransactionProtocol using:cardTenderProtocol delegate:transactionProcessingDelegate];

Best Practices

Disable iOS Auto-lock Timer During Transaction Processing

Some iOS users may have configured the auto-lock timer of their devices with low values in order to preserve battery life. Since transactions can last for an undetermined amount of time due to factors involving cardholder responsiveness, it is a good idea to prevent the device from going to sleep in the middle of a transaction. In the sample application, you will notice that this technique is used after starting a transaction. Then the auto-lock timer functionality is restored upon completion of a transaction. Here is an example:

// disable the auto-lock timer
[UIApplication sharedApplication].idleTimerDisabled = YES;

// (do important tasks)

// re-enable the auto-lock timer
[UIApplication sharedApplication].idleTimerDisabled = NO;

Client Certificate for IP Communication

Commerce SDK integrators receive the following client certificate files from Elavon:

  • Client_CERT.PEM - Client Certificate
  • Client_KEY.PEM - Certificate's Private Key
  • CA.PEM - Certificate Authority

Use the 3 .PEM files to create a P12 file, which is required during application runtime. To convert the PEM certificate file and the private key to PKCS#12 (.pfx .p12), run this command and enter a password when prompted:

  • openssl pkcs12 -export -out CLIENT.p12 -inkey CLIENT_KEY.PEM -in CLIENT_CERT.PEM -certfile CA.PEM

Add the generated CLIENT.P12 file to the resource bundle of the application.

The CLIENT.P12 file and password are used in the application during construction of an ECLInetAddres object. An example is available in the AccountDelegate.m file of the sample application.