Skip to main content

Payments

An easy to integrate solution to accomplish payments, refunds and cancellations alongside a card reader

Overview

Payments SDK provides functionalities to be able to connect to a hardware card reader, and perform transactions using a credit or debit card with any read mode (magnetic band, EMV chip, or NFC contactless )

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate the SDK into your Xcode project using CocoaPods, first add the souces to the top of your Podfile:

source 'https://bitbucket.org/geopagos-sdk/ios-specs.git'
source 'https://github.com/CocoaPods/Specs.git'

Then add the pod to your target, as an example we use version 16.0.22:

  pod 'Payments', '16.0.22'

If you want to use magicpos reader then you need to add its pod. You can specify a version in case you needed to

  pod 'MagicPosHardware', '10.0.18'

If you want to use qpos reader then you need to add its pod. You can specify a version in case you needed to

  pod 'QPosHardware', '10.0.18'

Example full Podfile, if your target is named MyTarget:

source 'https://bitbucket.org/geopagos-sdk/ios-specs.git'
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '13.0'

target 'MyTarget' do
use_frameworks!

# Pods for MyTarget
pod 'Payments', '16.0.22'
pod 'MagicPosHardware', '10.0.18'
pod 'QPosHardware', '10.0.18'

end

Using the Payments SDK

Importing the SDK

  • Import the Payments framework in your integrating class or struct
import Payments
  • Create an instance of PaymentsConfiguration to configure the endpoint, readerInstallers (optionally a logger object, plugins and SwipePinDecider implementation)
  • Call PaymentsSDK.configure(...) to configure the SDK
do {
let paymentsConfiguration = try PaymentsConfiguration.Builder(
endpoint: <A valid URL>,
readerInstallers: [
<QposReaderInstaller>,
<MagicPosReaderInstaller>
]
).build()

PaymentsSDK.configure(
paymentsConfiguration: paymentsConfiguration
)
} catch {
// Error initializing SDK
}

Note: To create readerInstallers it is necessary to import the module that corresponds to each Reader

For Magic Pos

import MagicPosHardware 

let magicPosReaderInstaller = MagicPosReaderInstaller()

For Qpos

import QPosHardware

let qposReaderInstaller = QposReaderInstaller()

Scan for readers

To begin a transaction, first we need to scan for the available bluetooth card readers. To do so we instantiate the class DeviceScanService

let deviceService = DeviceScanService()

deviceService.scan { (result: DeviceResult) in
switch result {
case let .success(devicesList, isScanning):
for device: Device in devicesList {
// Select and retain the reader's device that will be used during the transaction
}
case let .error(errorType, isScanning):
// handle errors
}
}

to stop scanning for readers call

   deviceService.stopScanning()

Once you have selected a reader from the scannedd readers list, a transaction can be started

Start a transaction

To start a transaction we need to create a TransactionIntent instance, using the TransactionIntentFactory class

TransactionIntentFactory.createTransactionIntent(
type: <A TransactionType>,
listener: <A SDKTransactionListener Implementation>,
completion: { [weak self] result in
switch result {
case let .success(aTransactionIntent):
// Retain the transaction
self?.transaction = aTransactionIntent
case let .failure(error):
// Do something with the error
}
}
)

This method recieves 3 arguments:

  • TransactionType : (.payments, .refund, .cancel)
  • SDKTransactionListener : Reader and transaction states listener
  • (TransactionIntentResult) -> Void : Completion block which receives the result of the transaction creation (success or failure) as argument

Once the transactionIntent is created, the listener injected in the transaction creation, that conformed to SDKTransactionListener will begin to be called, representing states of the recently created transaction. The SDKTransactionListener methods guide you through the data that must be provided

The implementation of SDKTransactionListener will start being updated with changes of states of both the reader and transaction

Listening to reader states

  • the method onReaderStateChanged provides feedback for the reader states
    func onReaderStateChanged(state: ReaderState) {
switch state {
case let .notConnected:
case let .connecting(device):
case let .connected(device, readerInfo, .idle):
case let .connected(device, readerInfo, .waitingForCard):
case let .connected(device, readerInfo, .processing(mode)):
case let .connected(device, readerInfo, .emvRequestingPin):

@unknown default:
break
}
}

Listening to transaction states

func onTransactionStateChanged(state: SDKTransactionState) {
switch state {
case let .provideDevice(provideDeviceCallback: provideDeviceCallback):
// select a device from the scanned devices and pass it in the callback
provideDeviceCallback(aSelectedDeviceFromTheScannedList)

case let .provideReadConfig(provideReadConfigCallback: provideReadConfigCallback):
// Provide a reader configuration containing information the reader needs to perform the transaction
let transactionTotal = TransactionTotal(...)
let readConfig = ReadConfig(
readModes: [.swipe, .chip, .nfc],
timeout: 10,
transactionTotal: transactionTotal
)
provideReadConfig(readConfig)

case let .selectEMVApp(availableAIDs: availableAIDs, selectEMVAppCallback: selectEMVAppCallback):
// Select an EMV app if more than one are available, and pass it in the callback
if let emvApp = availableAIDs.first {
selectEmvApp(emvApp)
}

case let .confirmPayment(card: card, confirmCallback: confirmCallback, rejectCallback: rejectCallback):
// After reading the card for a Sale transaction, a confirmation is requested, in order for the transaction to go online
let confirmation = Confirmation(...)
let paymentConfirmation = PaymentConfirmation(confirmation: confirmation, installments: nil, paymentPlan: nil)
accept(paymentConfirmation)

case let .confirmRefund(card: card, confirmCallback: confirmCallback, rejectCallback: rejectCallback):
// After reading the card for a refund transaction, a confirmation is requested, in order for the transaction to go online
let confirmation = Confirmation(...)
let refundConfirmation = RefundConfirmation(confirmation: confirmation, parentRefNumber: parentRefNumber)
accept(refundConfirmation)

case let .confirmCancel(card: card, confirmCallback: confirmCallback, rejectCallback: rejectCallback):
// After reading the card for a refund transaction, a confirmation is requested, in order for the transaction to go online
let confirmation = Confirmation(...)
let cancelConfirmation = CancelConfirmation(confirmation: confirmation, parentRefNumber: parentRefNumber)
accept(cancelConfirmation)

case let .approved(transactionApproved: transactionApproved):
switch transactionApproved {
case let .sale(transactionCard, saleConfirmation, paymentData):
// Sale approved
case let .refund(transactionCard, refundCOnfirmation, paymentData):
// Refund approved
case let .cancel(transactionCard, cancelConfirmation, paymentData):
// Cancel approved
@unknown default: break
}

case let .recoverableError(error: error, recoverCallback: recoverCallback):
// A recoverable error occurred in any of the transaction states. A `TransactionIntentError` error is provided along with a recoverable callback

case let .nonRecoverableError(error: error, message: message):
// A Non recoverable error occurred in any of the transaction states, A `TransactionIntentError` error is provided. Since this error can not be recovered, a new transaction must be started

@unknown default:
break
}
}

Reader Configuration and firmware Update

Reader configuration can be updated using the appropriate configuration files provided by Geopagos team.

To create a ReaderConfigurator, use the ReaderConfiguratorFactory

let readerConfigurator = ReaderConfiguratorFactory.createConfigurator(
delegate: delegate
)

Where the delegate implements the ReaderConfiguratorDelegate protocol. Each delegate callback will guide you into completing the configuration, and asking for needed data.

On the onConfigurationRequired method, you must provide a ReaderConfigurationFiles objects, different factories must be used, depending on the configuration type and the reader to be updated. Firmware configuration can be done as follows:

For Qpos

QposReaderConfigurationFactory.qposFirmwareFiles(firmware: url)

where firmware is the URL to the qPos bundle file provided by geopagos

For Magicpos

MagicPosReaderConfigurationFactory.qposFirmwareFiles(firmware: url)

and the URL to the magicPos bundle file provided by geopagos

If the bundle file does not contain a configuration that matches with the configuration type and the reader requested, a recoverableError of type .invalidConfiguration will be called.

Keeping the Reader charging

Some configuration updates result in a lengthy operation (specially firmware updates) that can take between 4 and 10 minutes to complete. Because of this, it's recommended to ask the user to keep the reader connected to a power source before start updating to avoid a disconnection in the middle of the update due to low battery charge. In the qpos reader it is mandatory to connect it to a power source when firmware updates are done

Configure Pin decider

In case of magnetic stripe transactions, it is possible to provide custom logic for requesting pin, To do so, SwipePinDecider protocol must be implemented.

struct CustomtSwipePinDecider: SwipePinDecider {

func decide(card: TransactionCard) -> Bool {
//...
}
}

the decide method receives the card data and returns a boolean, this custom implementation may be provided during SDK configuration.


let paymentsConfiguration = PaymentsConfiguration.Builder(
endpoint: <A valid URL>,
readerInstallers: [
<QposReaderInstaller>,
<MagicPosReaderInstaller>
]
)
.setSwipePinDecider(
swipePinDecider: customtSwipePinDecider
)
.build()

PaymentsSDK.configure(
paymentsConfiguration: paymentsConfiguration
)

Configure logger

The Payments SDK provides a mechanism for collecting logs, Protocol SDKLogger must be implemented as follows:

struct CustomSDKLogger: SDKLogger {
func log(message: String, level: LogLevel) {
//...
}
}

the log method receives the log message and log level(the logs can be printed through the console, saved in a file, etc), this custom implementation may be provided during SDK configuration

    let paymentsConfiguration = PaymentsConfiguration.Builder(
endpoint: <A valid URL>,
readerInstallers: [
<QposReaderInstaller>,
<MagicPosReaderInstaller>
]
)
.setLogger(logger: customLogger)
.build()

PaymentsSDK.configure(
paymentsConfiguration: paymentsConfiguration
)