Objective-C (iOS)
In this topic:
Requirements
- iPad and iPhone are supported.
- Device must be running iOS 11 or later.
- Device cannot be jailbroken.
Build the Sample Application
Install Xcode version 14 or later on your Mac from the Mac App Store.
Refer to the open_in_newXcodeLink opens new window support page for more information.
Copy the
CommerceConverge-SampleAppDebug.zip
file and unzip on the command line usingunzip CommerceConverge-SampleAppDebug.zip -d outputFolder
, where you replaceoutputFolder
with the folder you would like all the files to be located.info_outlineimportant
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
.Open Xcode and open
CommerceSample.xcodeproj
using the File/Open menu.Connect your iOS device to your Mac.
On the toolbar where it shows what device you are building for click on it and choose your device you just connected. Also make sure the build scheme is set to
CommerceConvergeSample
.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
. Click the Signing & Capabilities tab. - 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.
- In the project navigator, click
Press Command+B to build the sample application.
Press Command+R to run the sample application.
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.
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. You can scroll down on the list of buttons in the sample app to reveal more functions.
If you want to run the sample app with the release build of the frameworks, run
unzip CommerceConverge-FrameworkRelease.zip
, and copy the frameworks usingcp -a
over the debug frameworks including in the sample app zip file.info_outlineimportant
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
There are several required frameworks you need to link with your application:
Commerce-Converge.xcframework
CommerceDataTypes.xcframework
CardReaderWrapperDynamic.xcframework
ElavonCommon.xcframework
RUA_MFI.xcframework
LandiSDK_MFI.xcframework
StarIO.framework
There are also other frameworks you will need to link, depending on the card readers you plan to support for your app. If you do not support any of the below card readers, then don’t include the corresponding framework(s) otherwise it will increase your app size.
- Ingenico iCMP
CommerceIcm122.xcframework
- Ingenico iSC250
CommerceIsc250.xcframework
- Ingenico iPP320
CommerceIpp320.xcframework
CommerceIpp320v4.xcframework
- Ingenico Link/2500
CommerceLink2500.xcframework
CommerceLink2500v2.xcframework
- Ingenico Lane/3000
CommerceLane3000.xcframework
- Ingenico Lane/5000
CommerceLane5000.xcframework
info_outlinenote
There are no additional frameworks for other card readers such as Ingenico RP457c or Ingenico Moby/5500.
- Ingenico iCMP
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 frameworks into a folder under your application project location.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.
Under Build Options, make sure Enable Bitcode is set to No.
Under Other Linker Flags, click + and add
-ObjC
.Under Target, click your target name. Make sure you are under the General tab. Under Frameworks, Libraries, and Embedded Content, click +. In the dialog, click the Add Other… button then Add Files… button, and choose all the frameworks from above.
Make the appropriate selection for the “Embed” option based on the framework (see sample app for reference):
Framework Embed Option Commerce-Converge.xcframework
Do Not Embed CommerceDataTypes.xcframework
Embed & Sign CardReaderWrapperDynamic.xcframework
Embed & Sign ElavonCommon.xcframework
Embed & Sign RUA_MFI.xcframework
Do Not Embed LandiSDK_MFI.xcframework
Do Not Embed StarIO.framework
Do Not Embed For all card reader frameworks from step 2 above, select Embed & Sign. For all other frameworks, select Do Not Embed.
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 yourPLIST
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>
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 Signing & Capabilities tab.
Click on the + Capability button and add 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 add Wireless Accessory Configuration if not already there and click Fix issues if needed.
Click the Info tab (which corresponds to your PLIST file) and click the + button under Custom iOS Target Properties.
Add Privacy - Bluetooth Always Usage Description and Privacy - Microphone Usage Description.
Add 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
- Ingenico card readers (iCMP, Link/2500)
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; }
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.
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.
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 }
Implement the
ECLConvergeAccountDelegate
protocol.Code to implement could look like this:
// In your header: #import <Commerce-Converge/ECLConvergeAccountDelegate.h> @interface ConvergeAccountDelegate : NSObject <ECLConvergeAccountDelegate> @end // 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 *)partnerId:(id<ECLAccountProtocol>)account { // returns your Converge account's partner app ID if you have one. // this is optional and can be nil. } - (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 }
In 5.5, the following methods were added.
- (void)deviceCertificateUpdate:(id<ECLAccountProtocol>)account certificateInfo:(ECLCertificateInfo *)certificateInfo { // do something - look in the sample app code for a concrete example } - (ECCSensitiveData *)bridgeMaintenanceSystemUsername:(id<ECLAccountProtocol>)account { // return the bmsUsername - each integrator will have unique BMS credentials - get them from your solution engineer } - (ECCSensitiveData *)bridgeMaintenanceSystemPassword:(id<ECLAccountProtocol>)account { // return the bmsPassword - each integrator will have unique BMS credentials - get them from your solution engineer } - (void)bridgeMaintenanceSystemFailure:(id<ECLAccountProtocol>)account error:(NSError *)error { // BMS is the system used to get TMS updates. For example BMS provides updated certificates. // // WARNING WARNING WARNING // !!! Something went wrong when trying to communicate with BMS. !!! /// !!! If the failure is ECLAccountInvalidCredentials it MUST be addressed. !!! /// !!! Valid BMS credentials are required in order to keep certificates up to date. !!! /// !!! No transactions will be processed if connecting over IP without valid BMS credentials. !!! /// } @end
Now you have an
ECLConvergeAccountDelegate
that you can pass to create an account in the next step.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 forECLConvergeAccountDelegate
for what methods need to be implemented. The queue parameter is thedispatch_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.
info_outlinenote
You have to wait for
accountDidInitialize
on yourECLConvergeAccountDelegate
implementation before you will have an account instance to use for transactions.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.
Implement
ECLTransactionProcessingDelegate
.Refer to the
MainViewController
class in the sample application and the [doclink id=“continue-transaction”]Continue Transaction[/doclink] 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> @end // 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 } @end
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
POS must implement certificateUpdateRequired()
. In this method, POS should
- store the certificate files in the application’s documents directory
- upon connecting, supply information in connection criteria (see [doclink id=“device-connection-criteria” product=“commerce-sdk”]Device Connection Criteria[/doclink])
- supply certificate files using
setConnectionCriteria
where the certificate information is stored in an ECLInetAddress
- supply certificate files using
POS must implement connectWithUpdatedKeystoreInfo()
. In this method POS should
- call
[ECLCardReaderProtocol connectWithUpdatedKeystoreInfo]