Skip to main content

How to update readers

This section provides a technical explanation of how to update readers using the SDK in iOS, Android, and React Native. Below, you will find step-by-step instructions to manage a reader update — from creating the configurator to providing the device and configuration.

⚠️ Note: This process applies only to MagicPos and Qpos devices.

These steps cover updates for both EMV configuration and firmware.

Create a Configurator

The first step is to create a Configurator, which will be responsible for managing the update process.

To create a ReaderConfigurator, use the ReaderConfiguratorFactory.

let readerConfigurator = ReaderConfiguratorFactory.createConfigurator(delegate: delegate)

Each platform requires specific listeners or delegates to handle events during the update process.

During the update process, two important callbacks handle different stages and statuses(for android implement SDKReaderConfiguratorListener and react-native call readerConfigurator.start()):

  1. onReaderConfiguratorStateChanged → Handles configuration process states.
  2. onReaderStateChanged → Handles reader connection and transaction states.

1.Configuration States (onReaderConfiguratorStateChanged), these states guide the flow of the update process:

  • DeviceRequired
    A device must be provided to start the update process.

  • ConfigurationRequired
    Configuration files (EMV/Firmware) must be provided to continue.

  • RecoverableError
    A recoverable error occurred. You can attempt to continue the process by handling this error.

  • NonRecoverableError
    A critical error occurred. The update process cannot continue.

  • ConfigurationCompleted
    The update process (EMV/Firmware) finished successfully.

2.Reader States (onReaderStateChanged), these states reflect the connection and transaction status of the reader:

  • Connecting
    The reader is attempting to connect to the selected device.

  • Connected.Idle
    The reader is connected and ready for operations.

  • Connected.WaitingForCard
    The reader is connected and waiting for a card to be presented.

  • Connected.Processing.Card
    The reader is connected and processing a card transaction.

  • Connected.Processing.Pin
    The reader is connected and awaiting PIN entry.

  • Connected.UpdatingConfiguration
    The reader is connected and updating its EMV/Firmware configuration.

  • NotConnected
    The reader is not connected to any device.

In ios implement the ReaderConfiguratorDelegate protocol to handle the update flow:

  • onReaderStateChanged to handle reader configurator states
  • onDeviceRequired to provide the device
  • onConfigurationRequired to provide the configuration
  • onConfigurationCompleted called when the configuration is completed
  • onRecoverableError when a recoverable error appear
  • onNonRecoverableError when a onNonRecoverableError error appear
public protocol ReaderConfiguratorDelegate: AnyObject {

func onReaderStateChanged(state: ReaderState)

func onDeviceRequired(provideDevice: @escaping DeviceRequiredCallback)

func onConfigurationRequired(configuration: @escaping ConfigurationRequiredCallback)

func onConfigurationCompleted()

func onRecoverableError(error: ReaderConfiguratorError, recover: @escaping RejectRecoverCallback)

func onNonRecoverableError(error: ReaderConfiguratorError, message: String?)
}

Provide Device

Once requested, you must provide the Device previously scanned to be updated.

Use the provideDevice callback when onDeviceRequired is called:

public protocol ReaderConfiguratorDelegate: AnyObject {    
func onDeviceRequired(provideDevice: @escaping DeviceRequiredCallback)
}

public typealias DeviceRequiredCallback = (Device) -> Void

func onDeviceRequired(provideDevice: @escaping DeviceRequiredCallback) {

if let device = device {
if device.name.hasPrefix("SR") {
provideDevice(device)
return
}

let deviceConfiguration = DeviceConfigurationBuilder()
.setTerminalCapabilities("6020C8")
.setCountryCode("0032")
.build()

do {
let device = try DeviceConfigurator.deviceWithConfiguration(
device: device,
deviceConfiguration: deviceConfiguration
)
provideDevice(device)
} catch {
print(error)
}
}
}

Provide Configuration

You need to provide the correct configuration files to proceed with the update. The configurations required to update the reader (EMV parameters or firmware) are provided as bundles compressed files (ZIP format) containing all necessary assets for the update process. Geopagos delivers these configuration bundles directly to each partner.

On onConfigurationRequired, you must provide a ReaderConfigurationFiles object, using the corresponding factory depending on the reader and configuration type:

public protocol ReaderConfiguratorDelegate: AnyObject {
func onConfigurationRequired(configuration: @escaping ConfigurationRequiredCallback)
}

public typealias ConfigurationRequiredCallback = (ReaderConfigurationFiles) -> Void

func updateConfiguration(
configurationType: ConfigurationType,
bundleName: String? = nil,
configuration: @escaping ConfigurationRequiredCallback
) {
let bundleName = bundleName != nil ? bundleName : "configurationBundle"
guard let url = getURLFile(resource: bundleName!, type: "zip") else {
fatalError("could not get url from configurationBundle")
}

switch configurationType {
case .qposFirmware:
let readerConfigurationFiles = QposReaderConfigurationFactory.qposFirmwareFiles(
firmware: url
)
configuration(readerConfigurationFiles)

case .qposEmv:
let readerConfigurationFiles = QposReaderConfigurationFactory.qposEmvFiles(
emvConfiguration: url
)
configuration(readerConfigurationFiles)

case .qposFirmwareEmv:
let readerConfigurationFiles = QposReaderConfigurationFactory.qposFirmwareEmvFiles(
firmwareEmv: url
)
configuration(readerConfigurationFiles)

case .magicPosFirmware:
let readerConfigurationFiles = MagicPosReaderConfigurationFactory.magicPosFirmwareFiles(
firmware: url
)
configuration(readerConfigurationFiles)

case .magicPosEmv:
let readerConfigurationFiles = MagicPosReaderConfigurationFactory.magicPosEmvFiles(
emvConfiguration: url
)
configuration(readerConfigurationFiles)

case .magicPosFirmwareEmv:
let readerConfigurationFiles = MagicPosReaderConfigurationFactory.magicPosFirmwareEmvFiles(
firmwareEmv: url
)
configuration(readerConfigurationFiles)
}
}

QPOS

QposReaderConfigurationFactory.qposFirmwareFiles(firmware: url)

Where firmware contains the bundle file provided from Geopagos.

MagicPOS

MagicPosReaderConfigurationFactory.qposFirmwareFiles(firmware: url)

Firmware configuration is the same as QPOS.

In the case that the bundle file does not contain a configuration that matches the configuration type and the reader requested, it will return a .invalidConfiguration error.

Qpos mini Firmware update (Edge case)

In case Firmware update failed and reader passes to boot state (non responsive to any pressed key) it is necessary to use a special device to finish the update, to do so we need to add a special mapper first

In the case Firmware update fails, the reader is left in a “boot state” (not response to press any key). In this case it is necessary to use a special device to finish the update.

let readerConfigurator = try? ReaderConfiguratorFactory.createConfigurator(
delegate: self,
additionalMappers: [
QposMiniFirmwareRestoreDeviceMapper()
]
)

and create a QposMiniFirmwareRestoreDevice

 // Example:
let readerInfo = ReaderInfo(
id: "",
serial: "",
batteryInfo: BatteryInfo(
percentage: 1.0,
charging: false
),
firmware: "",
hardwareVersion: "A27C_P1",
readerType: .qposMini
)
let qposMiniFirmwareRestoreDevice = QposMiniFirmwareRestoreDevice(
device: device,
readerInfo: readerInfo
)

then the device should be passed in ReaderConfiguratorDelegate.onDeviceRequired(provideDevice:)

Update Error

During an update, two kind of errors can occur: recoverable and not recoverable.

Recoverable Error

In the recoverable, it’s possible to retry the update calling the associate callback, in the not recoverable errors, the update operation failed and it's necessary to start the process again.

On the onRecoverableError callback, use the recover() method to recover.

public protocol ReaderConfiguratorDelegate: AnyObject {
func onRecoverableError(error: ReaderConfiguratorError, recover: @escaping RejectRecoverCallback)
}

extension ViewController: ReaderConfiguratorDelegate {
func onRecoverableError(error: ReaderConfiguratorError, recover: @escaping RejectRecoverCallback) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
recover()
}
}
}

Nonrecoverable Error This state indicates that an error has occurred and its NonRecoverable.

Check for onNonRecoverableError().

public protocol ReaderConfiguratorDelegate: AnyObject {
func onNonRecoverableError(error: ReaderConfiguratorError, message: String?)
}

extension ViewController: ReaderConfiguratorDelegate {
func onNonRecoverableError(error: ReaderConfiguratorError, message: String?) {
print("Non Recoverable Error \(error)")
}
}

Update completed

This step indicates that the reader configuration process has been finished.

Check the method onConfigurationCompleted().

extension ViewController: ReaderConfiguratorDelegate {

func onConfigurationCompleted() {
print("Completed!")
}
}

public protocol ReaderConfiguratorDelegate: AnyObject {
func onConfigurationCompleted()
}