Skip to main content

iOS SDK (HiLite) — Integration Guide

AI coding agents

Fetch the integration-path skill for machine-readable setup guidance and code examples: /.well-known/skills/paths/ios-hilite.md

What is this integration path?​

The iOS HiLite path runs your iOS application on an iPhone or iPad and communicates with a HiLite card reader via Bluetooth or Lightning. Your POS app lives on the iOS device; the HiLite handles card reading and P2PE encryption.

Choose this path for mobile merchants on iPhone or iPad who need to accept payments anywhere.

When to use it​

✅ Good fit❌ Not a good fit
Your app runs on iPhone or iPad with a HiLite readerYou need Android — use the Android HiLite path
Merchants need to accept payments away from a fixed counterYou need a fixed PAX terminal — use the Cloud API
You want a compact, battery-powered reader—
Back-office operations are always available

Backoffice REST API operations — tip adjustment, reversals, refunds, MOTO charges, batch management, deferred tokenization — are available alongside any integration path you choose. They go server-side directly to the payment gateway with no terminal or SDK required. Subject only to acquirer support.

Capabilities not available on HiLite​

  • Pre-authorization — no on-device pre-auth flow
  • MOTO / remote sale on-terminal — HiLite has no manual entry keypad
  • getTransactionStatus — not available on iOS SDK

How it works​

Your iOS App (iPhone / iPad)
│ heftClient.saleWithAmount:currency:cardholder:
▼
Handpoint iOS SDK (HeftManager)
│ Bluetooth or Lightning
▼
HiLite Card Reader
│ chip / tap / swipe + P2PE
▼
Acquirer / Card Network (via mobile data or Wi-Fi)
│
▼
responseFinanceStatus: (result delegate callback)

Authentication​

CredentialPurposeProvisioned by
sharedSecretAuthenticates your app to the HiLite readerHandpoint Integration Support

Setup​

1. Request credentials and hardware​

Contact your Handpoint Integration Support engineer for:

  • A merchant sharedSecret
  • A HiLite Bluetooth or Lightning reader

2. Add the SDK​

CocoaPods:

# Podfile
pod 'HandpointSDK'

Carthage / Swift Package Manager: contact Integration Support for the framework URL.

Manual:

  • Add HandpointAll.h (header) and libheft.a (library) to your project.
  • Or use HandpointSDK.framework for the Carthage build.

3. Configure Info.plist​

<!-- External accessory protocol (Lightning HiPro) -->
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.datecs.pinpad</string>
</array>

<!-- Background mode (keeps session alive when app goes to background) -->
<key>UIBackgroundModes</key>
<array>
<string>external-accessory</string>
</array>

<!-- Bluetooth (required for BT HiLite, iOS 13+) -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Used to connect to the HiLite card reader</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Used to connect to the HiLite card reader</string>

4. Configure Build Settings​

  • Other Linker Flags: add -lc++
  • Build Active Architecture Only: YES

5. Implement delegates and initialise​

// ViewController.m

#import "HandpointAll.h"

@interface ViewController () <HeftDiscoveryDelegate, HeftStatusReportDelegate>
@property (nonatomic, strong) HeftManager *manager;
@property (nonatomic, strong) id<HeftClient> heftClient;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.manager = [HeftManager sharedManager];
self.manager.delegate = self; // HeftDiscoveryDelegate

// Start Bluetooth discovery (skip for Lightning — use connectedCardReaders directly)
[self.manager startDiscovery];
}

// Discovery — fires for each Bluetooth device found
- (void)didFindAccessoryDevice:(HeftRemoteDevice *)newDevice {
// Show in picker, or auto-connect if known device
}

// Discovery complete — get the full list
- (void)didDiscoverFinished {
NSArray *devices = [self.manager connectedCardReaders];
HeftRemoteDevice *device = [devices firstObject];
if (device) {
NSString *secret = @"0102030405060708091011121314151617181920212223242526272829303132";
[self.manager clientForDevice:device sharedSecret:secret delegate:self];
}
}

// HeftStatusReportDelegate — connection established
- (void)didConnect:(id<HeftClient>)client {
self.heftClient = client;
// Safe to start financial operations
}

// Transaction result
- (void)responseFinanceStatus:(id<FinanceResponseInfo>)info {
NSString *status = info.statusMessage;
BOOL approved = [status isEqualToString:@"AUTHORISED"];
}

// Status updates during a transaction
- (void)responseStatus:(id<ResponseInfo>)info { }

@end

Swift:

import HandpointSDK // or import HandpointAll

class ViewController: UIViewController, HeftDiscoveryDelegate, HeftStatusReportDelegate {

var manager: HeftManager!
var heftClient: HeftClient?

override func viewDidLoad() {
super.viewDidLoad()
manager = HeftManager.sharedManager()
manager.delegate = self
manager.startDiscovery()
}

func didConnect(_ client: HeftClient!) {
heftClient = client
}

func responseFinanceStatus(_ info: FinanceResponseInfo!) {
let approved = info.statusMessage == "AUTHORISED"
}
}

Connecting for Lightning (HiPro)​

Skip discovery — the reader is already accessible:

HeftRemoteDevice *device = [[self.manager connectedCardReaders] firstObject];
NSString *secret = @"0102030405060708091011121314151617181920212223242526272829303132";
[self.manager clientForDevice:device sharedSecret:secret delegate:self];

Your first transaction​

// Amount in smallest currency unit — £10.00 = 1000
[self.heftClient saleWithAmount:1000 currency:@"GBP" cardholder:YES];
heftClient?.saleWithAmount(1000, currency: "GBP", cardholder: true)

cardholder:YES / cardholder: true — pass YES for card-present transactions.

Reading the result​

- (void)responseFinanceStatus:(id<FinanceResponseInfo>)info {
if ([info.statusMessage isEqualToString:@"AUTHORISED"]) {
[self chargeCard];
} else if ([info.statusMessage isEqualToString:@"DECLINED"]) {
[self showDeclined];
}
// Also available: info.customerReceipt, info.merchantReceipt, info.transactionID
}

Transaction recovery​

No getTransactionStatus on iOS

The iOS SDK does not have a getTransactionStatus method. Recovery must be performed server-side via the Cloud API. This makes pre-call persistence of the transactionReference mandatory — it is the only key your server can use to locate the transaction.

Recovery flow when responseFinanceStatus does not fire (app backgrounded, Bluetooth drop, timeout):

  1. Before every call, generate a UUID v4 transactionReference and save it to your server.
  2. On timeout or app restart with a pending reference, your server calls:
GET https://transactions.handpoint.com/transactions/{transactionReference}/status
ApiKeyCloud: YOUR_MERCHANT_API_KEY
  1. Check finStatus in the response. If UNDEFINED or not yet returned, poll every 10 s.
  2. If AUTHORISED and your DB shows no completed record, send a reversal from your server:
POST https://cloud.handpoint.com/reversal
ApiKeyCloud: YOUR_MERCHANT_API_KEY
Content-Type: application/json

{ "originalGuid": "<transactionID from status response>" }
  1. Clear the pending reference.

The iOS app cannot recover directly — your backend must own the recovery path.

→ Full recovery reference: Back-office REST API

Simulator test amounts​

When testing with the SDK simulator (libheft.a from HeftSimulatorLibrary):

AmountBehaviour
1000Declined
2000User Cancelled
3000Signature Requested
Any otherApproved

For production trigger amounts (DEMO terminal), see Development Hardware: Testing with trigger amounts.


Transaction operations reference​

All transaction methods are called on heftClient (the id<HeftClient> returned in didConnect:). Each method returns BOOL — YES if the operation was successfully sent to the terminal. The actual result is delivered asynchronously through responseFinanceStatus:.

Sale​

saleWithAmount:currency:cardholder: — initiates a payment operation.

ParameterTypeRequiredNotes
amountNSIntegerYesAmount in minor currency unit (e.g. 1000 = £10.00)
currencyNSStringYesISO 4217 alpha code, e.g. @"GBP" or Currency.GBP.alpha
cardholderBOOLYesYES for card-present transactions
referenceNSStringNoCustomer reference string (returned in result)
optionsSaleOptionsNoExtended options: customerReference, merchantAuth, divideByMonths
// Plain sale
[api saleWithAmount:1000 currency:Currency.GBP.alpha cardholder:YES];

// Sale with options
SaleOptions *options = [SaleOptions new];
options.customerReference = @"ORDER-123";

// Multi-MID / custom merchant auth (optional)
MerchantAuth *auth = [MerchantAuth new];
Credential *cred = [Credential new];
cred.acquirer = [Credential getAcquirerFromString:@"acquirer"];
cred.mid = @"mid";
cred.tid = @"tid";
[auth add:cred];
options.merchantAuth = auth;

// Budget period — SureSwipe/Altech only (e.g. 3 months)
options.divideByMonths = @"3";

[api saleWithAmount:1000 currency:Currency.EUR.alpha options:options];

Events invoked: responseStatus, responseError, requestSignature, responseFinanceStatus


Sale and Tokenize​

saleAndTokenizeWithAmount:currency:options: — performs a sale and simultaneously tokenizes the card. The card token is returned in responseFinanceStatus via the CardToken field.

ParameterTypeRequiredNotes
amountNSIntegerYesAmount in minor currency unit
currencyNSStringYesISO 4217 alpha code
optionsSaleOptionsNoSame options as Sale
// Plain sale and tokenize
[api saleAndTokenizeWithAmount:1000 currency:Currency.EUR.alpha];

// With options
SaleOptions *options = [SaleOptions new];
options.customerReference = @"Your customer reference";
[api saleAndTokenizeWithAmount:1000 currency:Currency.EUR.alpha options:options];

Events invoked: responseStatus, responseError, requestSignature, responseFinanceStatus


Sale Reversal​

saleReversalWithAmount:currency:transactionId:options: — voids a previous sale. Amount, currency, and the original transactionId are required. Reversals can only be performed the same day as the original transaction.

ParameterTypeRequiredNotes
amountNSIntegerYesMust match the original sale amount
currencyNSStringYesMust match the original sale currency
transactionIdNSStringYeseFTTransactionID from the original sale result
optionsOptionsNocustomerReference
// Plain sale reversal
[api saleReversalWithAmount:1000
currency:Currency.EUR.alpha
transactionId:originalTransactionID
options:nil];

// With customer reference
Options *options = [Options new];
options.customerReference = @"Your customer reference";
[api saleReversalWithAmount:1000
currency:Currency.EUR.alpha
transactionId:@"00000000-0000-0000-0000-000000000000"
options:options];

Events invoked: responseStatus, responseError, responseFinanceStatus


Refund​

refundWithAmount:currency:transaction:options: — moves funds from the merchant account back to the cardholder. The transaction parameter links the refund to an original sale, which limits the maximum refundable amount.

ParameterTypeRequiredNotes
amountNSIntegerYesAmount in minor currency unit
currencyNSStringYesISO 4217 alpha code
transactionNSStringYeseFTTransactionID of the original sale (limits max refund)
optionsMerchantAuthOptionsNocustomerReference, merchantAuth
// Linked refund
[api refundWithAmount:1000
currency:Currency.EUR.alpha
transaction:@"00000000-0000-0000-0000-000000000000"];

// With options and merchant auth
MerchantAuthOptions *options = [MerchantAuthOptions new];
options.customerReference = @"Your customer reference";
MerchantAuth *auth = [MerchantAuth new];
Credential *cred = [Credential new];
cred.acquirer = [Credential getAcquirerFromString:@"acquirer"];
cred.mid = @"mid";
cred.tid = @"tid";
[auth add:cred];
options.merchantAuth = auth;

[api refundWithAmount:1000
currency:Currency.EUR.alpha
transaction:@"00000000-0000-0000-0000-000000000000"
options:options];

Events invoked: responseStatus, responseError, responseFinanceStatus


Refund Reversal​

refundReversalWithAmount:currency:transactionId:options: — voids a previous refund. Amount, currency, and the original refund's transactionId are required.

ParameterTypeRequiredNotes
amountNSIntegerYesMust match the original refund amount
currencyNSStringYesMust match the original refund currency
transactionIdNSStringYeseFTTransactionID from the original refund result
optionsOptionsNocustomerReference
Options *options = [Options new];
options.customerReference = @"Your customer reference";

[api refundReversalWithAmount:1000
currency:Currency.EUR.alpha
transactionId:@"00000000-0000-0000-0000-000000000000"
options:options];

Events invoked: responseStatus, responseError, responseFinanceStatus


Tokenize Card​

tokenizeCard — initiates a card tokenization operation without any payment. The card token is returned in the CardToken field of responseFinanceStatus. Not available for all acquirers — confirm with Handpoint.

[heftClient tokenizeCard];

Events invoked: responseStatus, responseError, requestSignature, responseRecoveredTransactionStatus


Accept Signature​

acceptSignature: — called in response to a requestSignature: delegate event during a transaction. Pass YES if the signature is valid, NO to decline.

ParameterTypeRequiredNotes
flagBOOLYesYES if signature accepted, NO if declined
// In your UI action handlers
- (IBAction)acceptSignatureTapped {
[heftClient acceptSignature:YES];
}

- (IBAction)declineSignatureTapped {
[heftClient acceptSignature:NO];
}

Events invoked: responseStatus, responseError, responseFinanceStatus


Retrieve Pending Transaction​

retrievePendingTransaction — fetches a transaction result that was lost due to a Bluetooth disconnection between the terminal and app. Call this immediately in didConnect: if isTransactionResultPending is YES.

Discard before new transaction

Starting a new transaction without first calling retrievePendingTransaction will discard the pending result permanently.

- (void)didConnect:(id<HeftClient>)client {
heftClient = client;

if ([heftClient isTransactionResultPending] == YES) {
// A pending transaction result was detected — fetch it
[heftClient retrievePendingTransaction];
}
}

The result is delivered via responseRecoveredTransactionStatus: (not responseFinanceStatus:).

Events invoked: responseStatus, responseError, responseRecoveredTransactionStatus


Tip Adjustment​

tipAdjustment — adjusts the tip amount of a completed sale before the day's batch is settled. This method does not use the card reader — it calls the Handpoint cloud API directly via HapiRemoteService.

Acquirer availability: EPI (TSYS, VANTIV) in the United States restaurant industry only. Limited to HiLite terminals.

ParameterTypeRequiredNotes
transactionNSStringYeseFTTransactionID (GUID) of the original sale
tipAmountNSIntegerYesTip amount in minor currency unit (e.g. 1000 = $10.00)
#include "HapiRemoteService.h"

// 1. Initialize the shared secret once
NSString *sharedSecret = @"0102030405060708091011121314151617181920212223242526272829303132";
BOOL initialized = setupHandpointApiConnection(sharedSecret);

// 2. Adjust tip on a completed sale ($10.00 tip)
NSString *transaction = @"d50af540-a1b0-11e6-85e6-07b2a5f091ec";

BOOL result = tipAdjustment(transaction, 1000, ^(TipAdjustmentStatus status) {
if (status == TipAdjustmentAuthorised) {
// Successfully adjusted
} else if (status == TipAdjustmentDeclined) {
// Declined by the processor
} else if (status == TipAdjustmentFailed) {
// System error or timeout — prompt user to retry
}
});

TipAdjustmentStatus values:

ValueMeaning
TipAdjustmentAuthorisedTip adjustment approved by the processor
TipAdjustmentDeclinedTip adjustment declined by the processor
TipAdjustmentFailedSystem error or timeout — retry recommended

If two tip adjustments are sent for the same transaction, the second overrides the first.


Stop / Cancel​

cancel — cancels the current in-progress transaction.

[heftClient cancel];

Device management reference​

sharedManager​

+ (HeftManager *)sharedManager — returns the singleton HeftManager instance. Call once to obtain the manager, set its delegate, then call startDiscovery.

HeftManager *manager = [HeftManager sharedManager];
manager.delegate = self;
[manager resetDevices]; // Clear previously discovered devices

startDiscovery​

- (void)startDiscovery — starts Bluetooth discovery. Each device found fires didFindAccessoryDevice:. When discovery completes, didDiscoverFinished fires. For Lightning (HiPro) readers skip discovery and use connectedCardReaders directly.

[heftManager startDiscovery];

clientForDevice:sharedSecret:delegate:​

- (void)clientForDevice:(HeftRemoteDevice *)device sharedSecret:(NSString *)sharedSecret delegate:(NSObject<HeftStatusReportDelegate> *)delegate — creates a HeftClient connection to a specific terminal. On success, the client is returned via didConnect:.

ParameterTypeRequiredNotes
deviceHeftRemoteDevice *YesDevice from connectedCardReaders or discovery
sharedSecretNSString *Yes64-character hex shared secret provided by Handpoint
delegateNSObject<HeftStatusReportDelegate> *YesObject to receive delegate callbacks
NSString *sharedSecret = @"0102030405060708091011121314151617181920212223242526272829303132";
[heftManager clientForDevice:[heftManager.connectedCardReaders firstObject]
sharedSecret:sharedSecret
delegate:self];

Events invoked: didConnect:


financeInit (Update Terminal)​

- (BOOL)financeInit — checks for and downloads available software or configuration updates for the terminal. Call after connecting if you want to ensure the terminal is up to date.

[heftClient financeInit];

Events invoked: responseStatus, responseError, responseFinanceStatus


logSetLevel​

- (BOOL)logSetLevel:(eLogLevel)level — sets the log level on the terminal.

LevelValueDescription
eLogNone0No logging
eLogError1Error messages only
eLogInfo2Informational only
eLogFull3Full logging (recommended for integration)
eLogDebug4Debug logging
[heftClient logSetLevel:eLogFull];

logGetInfo​

- (BOOL)logGetInfo — retrieves logs stored on the terminal. Result delivered via responseLogInfo:.

[heftClient logGetInfo];

logReset​

- (BOOL)logReset — clears logs stored on the terminal.

[heftClient logReset];

Barcode scanner (HiPro only)​

The barcode scanner API is available on HiPro (Lightning) terminals equipped with a barcode scanner. HiLite Bluetooth readers do not have a barcode scanner.

Enabling the scanner​

Call enableScanner (or one of its parameterised variants) to put the terminal into scan mode. The terminal waits for the scan button to be pressed and fires responseScannerEvent: for each code read.

// Enable with defaults — multi-scan on, button-activated, terminal-determined timeout
[self.heftClient enableScanner];

// Single-scan mode — scanner disables automatically after the first read
[self.heftClient enableScannerWithMultiScan:NO];

// Multi-scan, immediate (buttonless) activation, 30-second inactivity timeout
[self.heftClient enableScannerWithMultiScan:YES buttonMode:NO timeoutSeconds:30];

Receiving scan codes​

Implement responseScannerEvent: in your HeftStatusReportDelegate to receive each scanned barcode. This method is @optional.

- (void)responseScannerEvent:(id<ScannerEventResponseInfo>)info {
NSString *barcode = info.scanCode;
NSLog(@"Scanned: %@", barcode);
dispatch_async(dispatch_get_main_queue(), ^{
self.barcodeLabel.text = barcode;
});
}

- (void)responseScannerDisabled:(id<ScannerDisabledResponseInfo>)info {
// Scanner mode exited — update UI if needed
NSLog(@"Scanner disabled: %@", info.status);
}

Disabling the scanner​

Call disableScanner to exit scan mode explicitly. In single-scan mode (multiScan:NO) the terminal exits scan mode automatically after one read.

[self.heftClient disableScanner];

Events and delegate callbacks​

Your view controller must conform to two protocols: HeftDiscoveryDelegate (set on HeftManager) and HeftStatusReportDelegate (set on HeftManager.clientForDevice:…:delegate:).

HeftDiscoveryDelegate​

didFindAccessoryDevice:​

- (void)didFindAccessoryDevice:(HeftRemoteDevice *)newDevice;

Fired during Bluetooth discovery each time a new terminal is found. newDevice has name and address properties. Typically used to populate a device picker or auto-connect to a known device.

- (void)didFindAccessoryDevice:(HeftRemoteDevice *)newDevice {
NSLog(@"Found device: %@ address: %@", newDevice.name, newDevice.address);
// Auto-connect if this is the known device
}

didDiscoverFinished​

- (void)didDiscoverFinished;

Fired when Bluetooth discovery completes. Access all found devices via manager.connectedCardReaders.

- (void)didDiscoverFinished {
HeftRemoteDevice *device = [self.manager.connectedCardReaders firstObject];
if (device) {
[self.manager clientForDevice:device sharedSecret:sharedSecret delegate:self];
}
}

didLostAccessoryDevice:​

- (void)didLostAccessoryDevice:(HeftRemoteDevice *)oldDevice;

Fired when a previously-discovered Bluetooth terminal goes out of range or disconnects. Re-run discovery if reconnection is needed.


HeftStatusReportDelegate​

didConnect:​

- (void)didConnect:(id<HeftClient>)client;

Fired when a connection to a terminal is successfully established. The client parameter is the HeftClient to use for all subsequent transaction calls. Do not start any financial operations before this fires.

Also check isTransactionResultPending here to recover any pending transaction result.

- (void)didConnect:(id<HeftClient>)client {
heftClient = client;
NSLog(@"didConnect: mpedInfo=%@", client.mpedInfo);

if ([heftClient isTransactionResultPending]) {
[heftClient retrievePendingTransaction];
}
}

responseStatus:​

- (void)responseStatus:(id<ResponseInfo>)info;

Fired multiple times during a transaction with intermediate status updates from the terminal (e.g. "Waiting for card", "PIN entry", "Connecting to host"). Use to update your UI to show transaction progress.

info propertyTypeNotes
statusCodeintNumeric status code (see EFT_PP_STATUS table in skill)
statusNSStringHuman-readable status string
xmlNSDictionaryDetailed XML state of the terminal
- (void)responseStatus:(id<ResponseInfo>)info {
NSLog(@"responseStatus: code=0x%04lX status=%@", (long)info.statusCode, info.status);
dispatch_async(dispatch_get_main_queue(), ^{
self.statusLabel.text = info.status;
});
}

responseError:​

- (void)responseError:(id<ResponseInfo>)info;

Fired when an error occurs during a transaction. info.status contains the error message.

- (void)responseError:(id<ResponseInfo>)info {
NSLog(@"responseError: code=0x%04lX status=%@", (long)info.statusCode, info.status);
}

responseFinanceStatus:​

- (void)responseFinanceStatus:(id<FinanceResponseInfo>)info;

The primary transaction result callback. Fired when the terminal finishes processing a transaction. info is a FinanceResponseInfo object — see the FinanceResponseInfo reference below for all fields.

- (void)responseFinanceStatus:(id<FinanceResponseInfo>)info {
// Use toDictionary for a canonical log entry (all non-empty fields)
NSLog(@"responseFinanceStatus: %@", [info toDictionary]);

NSString *finStatus = info.finStatus; // "AUTHORISED", "DECLINED", "CANCELLED", etc.

if ([finStatus isEqualToString:@"AUTHORISED"]) {
// Store the eFTTransactionID — needed for reversal/refund
NSString *txId = info.eFTTransactionID;
[self fulfillOrder:txId];
} else if ([finStatus isEqualToString:@"DECLINED"]) {
[self showDeclined:info.errorMessage];
} else if ([finStatus isEqualToString:@"CANCELLED"]) {
[self showCancelled];
} else if ([finStatus isEqualToString:@"PARTIAL_APPROVAL"]) {
// US only — cardholder did not have enough funds
NSInteger authorised = info.authorisedAmount;
NSInteger due = [info.dueAmount integerValue];
[self collectRemainder:due];
}
}

requestSignature:​

- (void)requestSignature:(NSString *)receipt;

Fired during a financial operation when the terminal requires cardholder signature verification. The receipt parameter is the merchant receipt to display or print for the customer to sign.

signatureUrl handling

receipt may be either a URL (starting with http) pointing to an image, or a base64-encoded image binary (fallback when the upload fails). Always check with hasPrefix("http") before treating it as a URL.

- (void)requestSignature:(NSString *)receipt {
if ([receipt hasPrefix:@"http"]) {
// Load image from URL
NSURL *url = [NSURL URLWithString:receipt];
// ... display image
} else {
// Decode base64 image
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:receipt options:0];
UIImage *image = [UIImage imageWithData:imageData];
// ... display image
}

// After customer signs, call acceptSignature: YES or NO
[heftClient acceptSignature:YES];
}

cancelSignature​

- (void)cancelSignature;

Fired when the terminal cancels the signature request (e.g. cardholder timeout on the terminal side).


responseRecoveredTransactionStatus:​

- (void)responseRecoveredTransactionStatus:(id<FinanceResponseInfo>)info;

Fired in response to retrievePendingTransaction. info contains the recovered transaction result, or nil if no pending transaction was found. The recoveredTransaction field on the result is YES.

- (void)responseRecoveredTransactionStatus:(id<FinanceResponseInfo>)info {
if (info == nil) {
NSLog(@"No pending transaction found");
return;
}
NSLog(@"Recovered transaction: %@", [info toDictionary]);
// Treat as a normal responseFinanceStatus result
}

responseLogInfo:​

- (void)responseLogInfo:(id<LogInfo>)info;

Fired in response to logGetInfo. info.log contains the device log as a string.

- (void)responseLogInfo:(id<LogInfo>)info {
NSLog(@"deviceLog: %@", info.log);
}

Key objects​

HeftManager​

HeftManager is the entry point for the SDK. Obtain the singleton with [HeftManager sharedManager]. Set its delegate to receive HeftDiscoveryDelegate events.

PropertyTypeNotes
connectedCardReadersNSArrayAll discovered payment terminals
delegateNSObject<HeftDiscoveryDelegate>Discovery event receiver
versionNSStringCurrent SDK version

Key methods:

MethodNotes
+ sharedManagerReturns the singleton manager
- startDiscoveryStarts Bluetooth discovery
- clientForDevice:sharedSecret:delegate:Creates a HeftClient connection
- resetDevicesClears the discovered device list

HeftClient​

HeftClient is the transaction interface. Obtained via didConnect:. All financial operations are called on this object.

PropertyTypeNotes
sharedSecretNSStringThe shared secret used to authenticate
mpedInfoNSDictionaryTerminal details (serial number, firmware version, etc.)
isTransactionResultPendingBOOLYES if a previous transaction result is pending recovery

mpedInfo dictionary keys:

Key constantNotes
kSerialNumberInfoKeyTerminal serial number
kPublicKeyVersionInfoKeyPublic key version
kEMVParamVersionInfoKeyEMV parameters version
kAppNameInfoKeyTerminal application name
kAppVersionInfoKeyTerminal application version
kManufacturerCodeInfoKeyHardware manufacturer
kModelCodeInfoKeyHardware model

HeftRemoteDevice​

Returned during discovery; passed to clientForDevice:….

PropertyTypeNotes
nameNSStringDevice name (Bluetooth display name)
addressNSStringBluetooth address
accessoryEAAccessoryThe underlying EAAccessory object (Lightning only)

SaleOptions​

Options object for saleWithAmount and saleAndTokenizeWithAmount.

PropertyTypeNotes
customerReferenceNSStringMerchant-defined reference, returned unchanged in the result
merchantAuthMerchantAuthMulti-MID authentication credentials
divideByMonthsNSStringBudget period in months (SureSwipe/Altech only)

MerchantAuth / Credential​

Used for multi-MID scenarios where a single terminal can process transactions for multiple merchant accounts.

MerchantAuth *auth = [MerchantAuth new];
Credential *cred = [Credential new];
cred.acquirer = [Credential getAcquirerFromString:@"acquirer_name"];
cred.mid = @"merchant_id";
cred.tid = @"terminal_id";
[auth add:cred];
// Add multiple credentials if needed

FinanceResponseInfo​

The full transaction result object delivered to responseFinanceStatus:. Inherits from ResponseInfo.

PropertyTypeNotes
finStatusNSStringFinancial outcome: "AUTHORISED", "DECLINED", "CANCELLED", "FAILED", "PARTIAL_APPROVAL", "PROCESSED", "UNDEFINED"
financialResultNSIntegerNumeric code: 0x00=UNDEFINED, 0x01=APPROVED, 0x02=DECLINED, 0x03=PROCESSED, 0x04=FAILED, 0x05=CANCELLED
statusMessageNSStringHuman-readable result message (e.g. "AUTHORISED", "DECLINED")
typeNSStringTransaction type: "SALE", "REFUND", "VOID_SALE", "VOID_REFUND", "TOKENIZE_CARD", "SALE_AND_TOKENIZE_CARD"
authorisedAmountNSIntegerAmount authorised in minor currency unit
requestedAmountNSStringAmount originally requested
totalAmountNSStringTotal charged (requested + gratuity, or partial approval amount)
gratuityAmountNSStringTip amount entered by cardholder on terminal
gratuityPercentageNSStringTip as percentage of requested amount
dueAmountNSStringRemaining amount after partial approval (US only)
currencyNSStringCurrency used for the transaction
eFTTransactionIDNSStringUnique GUID for this transaction — store for reversal/refund
originalEFTTransactionIDNSStringGUID of the original transaction (refunds and reversals only)
transactionIdNSStringInternal transaction ID
eFTTimestampNSStringTransaction timestamp in ISO format (YYYYMMDDHHmmSS)
authorisationCodeNSStringApproval code from the processor
cardSchemeNameNSStringCard brand as emitted by terminal firmware (always uppercase): "VISA", "MASTERCARD", "AMEX", etc.
cardEntryTypeNSStringHow card was read: "ICC" (chip), "NFC" (tap), "MSR" (swipe)
verificationMethodNSStringCardholder verification: "PIN", "SIGNATURE", "NO_VERIFICATION"
CardTokenNSStringCard token (tokenization operations)
customerReceiptNSStringCustomer receipt in HTML format
merchantReceiptNSStringMerchant receipt in HTML format
errorMessageNSStringDetailed error reason (non-empty on DECLINED/FAILED)
customerReferenceNSStringCustomer reference passed at transaction start
budgetNumberNSStringBudget period reference (SureSwipe/Altech only)
chipTransactionReportNSStringFull EMV parameter report (if present)
balanceNSStringCardholder card balance (if acquirer supports it)
recoveredTransactionBOOLYES if this result is from a recovered pending transaction
isRestartingBOOLYES if the terminal will restart (e.g. after firmware update) — disconnect and wait for reconnect

FinancialStatus values:

finStatus stringfinancialResult hexMeaningAction
"AUTHORISED"0x01Transaction approvedStore eFTTransactionID, fulfil order
"DECLINED"0x02Declined by issuerShow decline, do not retry same card
"CANCELLED"0x05Cardholder or operator cancelledAllow retry
"FAILED"0x04Processing errorCheck errorMessage
"PARTIAL_APPROVAL"—Partial funds available (US only)Collect remaining via dueAmount
"PROCESSED"0x03Non-financial operation complete (e.g. financeInit)N/A
"UNDEFINED"0x00No result receivedDo not retry — recover server-side

Operations available​

OperationiOS HiLite support
Sale✅
Refund✅
Sale Reversal✅
Refund Reversal✅
Sale and Tokenize✅
Tokenize Card✅
Pre-Authorization❌
Tip Adjustment✅* remote API call (no device required)

* tipAdjustment() in HapiRemoteService calls the Handpoint cloud API directly — not a device command. Requires setupHandpointApiConnection(sharedSecret:) to be called first. Alternatively, use the Back Office integration guide server-side with ApiKeyCloud. Acquirers: EPI, PAYSAFE (non-Interac cards only).

Acquirer-specific availability: Acquirer capabilities matrix — ios-hilite column.

Validation & certification​

Required for every integration:

  • Bluetooth discovery and Lightning direct-connect both tested (if supporting both reader types)
  • didConnect gate verified — no financial operations before callback fires
  • isTransactionResultPending checked in didConnect: — retrievePendingTransaction called when pending
  • responseFinanceStatus: handles all finStatus values: AUTHORISED, DECLINED, CANCELLED, FAILED, PARTIAL_APPROVAL, UNDEFINED
  • eFTTransactionID stored on every AUTHORISED result — required for reversal/refund
  • requestSignature: implemented — hasPrefix("http") check before URL rendering
  • transactionReference generated and persisted to server before every call — required for server-side recovery (no getTransactionStatus on iOS SDK)
  • Server-side UNDEFINED recovery implemented and tested — GET /transactions/{ref}/status + auto-reversal
  • Background mode configured and app tested with screen locked during transaction
  • toDictionary log line emitted for every responseFinanceStatus: callback

→ Full scenario checklist: Validate your integration

→ Error codes: Error codes

See Also​