Use the Finix iOS SDK to integrate the PAX D135 Bluetooth card reader into your iOS app and accept in-person payments. This guide covers SDK installation, initialization, and processing your first transaction.
Before starting, complete the Integration Prerequisites to configure your environment, credentials, and a Device resource.
Add the SDK to your project via Swift Package Manager using:
https://github.com/finix-payments/finix-pax-mpos-ios-sdkPre-requisites
- iOS 15.6 or later
- iOS device. Simulator not supported
- Add Bluetooth permission in Info.plist using key Privacy -
Bluetooth Always Usage Description
To interact with the MPOS device, first initialize the SDK
import PaxMposSDK
let finixClient = FinixClient(config: FinixConfig(
environment: TEST_ENVIRONMENT,
credentials: Finix.APICredentials(username: TEST_USERNAME, password: TEST_PASSWORD),
application: TEST_APPLICATION,
version: TEST_VERSION,
merchantId: TEST_MERCHANT_ID,
mid: TEST_MERCHANT_MID,
deviceType: .Pax,
deviceId: "") //Device ID - device registers with Finix, starting with DV (i.e. DVxxxx)
)
finixClient.delegate = self
finixClient.interactionDelegate = selfOnce initialized, connect to the Device. When the Device is ready for pairing, it will show an orange light and green light. Once connected, the orange light will disappear and only the green light will stay on.
When first pairing a PAX D135 the initial configuration may take a few minutes to complete. We recommended that the user experience you build factors in the configuration time to ensure that the user knows the device configuration is in progress.
Call startScan() to scan for nearby D135 devices over Bluetooth. The didDiscoverDevice callback fires for each Device found.
finixClient.startScan()
extension ViewController: FinixDelegate {
func didDiscoverDevice(_ deviceInfo: DeviceInfo) {
devices.append(deviceInfo)
}
}Pass the selected device's Bluetooth ID (device.id) to connectDevice(). The deviceDidConnect callback fires when the connection is established.
finixClient.connectDevice(device.id)
extension ViewController: FinixDelegate {
func deviceDidConnect(_ deviceInfo: DeviceInfo) {
connectedDevice = deviceInfo
}
}Create a Device under the Merchant provisioned to process in-person payments. Include the model of the payment terminal you'll be using to process cards.
- Sandbox serverhttps://finix.sandbox-payments-api.com/merchants/{merchant_id}/devices
curl -i -X POST \
-u USfdccsr1Z5iVbXDyYt7hjZZ:313636f3-fac2-45a7-bff7-a334b93e7bda \
https://finix.sandbox-payments-api.com/merchants/MUwfZPNW3r4EqLMzwgr6txw4/devices \
-H 'Content-Type: application/json' \
-H 'Finix-Version: 2022-02-01' \
-d '{
"configuration": {
"allow_debit": true,
"allow_standalone_authorizations": false,
"allow_standalone_refunds": false,
"allow_standalone_sales": false,
"bypass_device_on_capture": true,
"check_for_duplicate_transactions": true,
"display_tip_on_receipt": false,
"prompt_amount_confirmation": true,
"prompt_manual_entry": false,
"prompt_receipt_confirmation": true,
"prompt_tip_on_screen": false,
"tipping_details": {
"allow_custom_tip": true,
"fixed_options": [
100,
150,
200
],
"percent_options": [
18,
20,
22
],
"percent_tipping_threshold": 0
}
},
"description": "Cashier Three",
"integration_mode": "PAYMENT_APP",
"model": "PAX_D135",
"name": "My PAX_D135 Finix Device",
"serial_number": "1904626094"
}'A successful response returns the Device resource you'll use to initialize the SDK.
{
"id": "DVxcL2fiBdt9frYCKAbZikZK",
"created_at": "2025-05-21T19:01:32.000581Z",
"updated_at": "2025-05-21T19:01:32.000581Z",
"configuration_details": {
"allow_debit": true,
"check_for_duplicate_transactions": true,
"prompt_amount_confirmation": true,
"prompt_manual_entry": false,
"signature_threshold_amount": 10000,
"bypass_device_on_capture": true,
"prompt_receipt_confirmation": true,
"display_tip_on_receipt": false,
"prompt_tip_on_screen": false,
"allow_standalone_authorizations": false,
"allow_standalone_sales": false,
"allow_standalone_refunds": false,
"tipping_details": {
"percent_tipping_threshold": 0,
"percent_options": [
18,
20,
22
],
"fixed_options": [
100,
150,
200
]
},
"idle_message": null,
"idle_image_file_id": null,
"automatic_receipt_delivery_methods": null,
"available_receipt_methods": null,
"prompt_for_signature": "NEVER",
"surcharge_basis_points": null
},
"description": "Cashier Three",
"enabled": false,
"idle_message": null,
"integration_mode": "PAYMENT_APP",
"merchant": "MU7noQ1wdgdAeAfymw2rfBMq",
"model": "PAX_D135",
"name": "My PAX_D135 Finix Device",
"serial_number": "19046260947",
"tags": {},
"_links": {
"self": {
"href": "https://finix.sandbox-payments-api.com/devices/DVxcL2fiBdt9frYCKAbZikZK"
},
"merchant": {
"href": "https://finix.sandbox-payments-api.com/merchants/MU7noQ1wdgdAeAfymw2rfBMq"
},
"transfers": {
"href": "https://finix.sandbox-payments-api.com/transfers"
},
"authorizations": {
"href": "https://finix.sandbox-payments-api.com/authorizations"
}
}
}Update the client with the Finix Device ID, then call startTransaction(). The device shows a blue light and prompts the customer to tap, swipe, or insert their card. Once the card is read, the light turns red, indicating the customer should remove their card.
finixClient.update(deviceId: "DVxxxxx")let transactionAmount = Currency(amount: Int(amountDouble * 100), code: .USD)
finixClient.startTransaction(
amount: transactionAmount,
type: transactionType,
splitTransfers: nil,
tags: nil,
buyerIdentityId: nil
) { transferResponse, error in
Task { @MainActor in
// Handle using transferResponse and error
}
}A referenced refund refunds the amount specified (refund amount) of a particular transaction (transaction ID) to the card on file.
/// Start a referenced refund
func startRefund(transactionID: String, amount: Currency, completion: ((RefundResponse?, Error?) -> Void)?) {
let mainCompletion: (RefundResponse?, Error?) -> Void = { response, error in
if let error {
self.logger.error("startRefund failed with error: \(error)")
}
DispatchQueue.main.async {
completion?(response, error)
}
}
let externalEndpoint = FinixAPIEndpoint.externalEndpoint(config.environment)
let endPoint = "transfers/\(transactionID)/reversals"
guard let url = URL(string: endPoint, relativeTo: externalEndpoint) else {
mainCompletion(nil, FinixError(code: .MalformedRequest, message: "Could not encode path!"))
return
}
guard let request = requestBuilder(url: url, method: .POST, payload: ["refund_amount": amount.amount]) else {
mainCompletion(nil, FinixError(code: .CannotEncodeParameters, message: "Cannot encode request parameters"))
return
}
finixRequest(request: request) { (response: RefundResponse?, error: Error?) in
print(response as Any, error as Any)
mainCompletion(response, error)
}
}If you encounter issues, use sendDebugReport to collect diagnostic information from the Device and send the logs to Finix Support to help troubleshoot the issue.
func sendDebugData() {}Alternatively, you can send debug logs using the Sample App.