Need help?

iOS

Overview

The Telr Mobile Payment SDK for iOS is a comprehensive payment solution that enables merchants to accept payments seamlessly within their iOS applications. Built with SwiftUI and following modern iOS development patterns, it provides a clean, secure, and customizable payment experience.

Key Features

  • Multiple Payment Methods: Credit/Debit Cards, Apple Pay
  • 3D Secure Support: Built-in 3DS authentication flow
  • Saved Cards: Tokenization and card management
  • Modern UI: SwiftUI-based interface with iOS design guidelines
  • Internationalization: Multi-language support (English, Arabic)
  • Security: PCI DSS compliant with tokenization
  • Accessibility: Full accessibility support for inclusive design

Requirements

  • iOS 15.1+
  • Swift 6.0+
  • Xcode 15.0+
  • CocoaPods 1.11+ (if using CocoaPods)
  • Your backend must return two URLs taken directly from the createOrder API response:
    • tokenUrl — value of _links.auth.href
    • orderUrl — value of _links.self.href

Installation

CocoaPods (Recommended)

  1. Install CocoaPods (if not already installed):

    sudo gem install cocoapods
  2. Create or update your Podfile:

    platform :ios, '15.1'
    
    use_frameworks!
    inhibit_all_warnings!
    
    target 'YourAppTarget' do
      pod 'TelrSDK', '~> 0.0.20'
    end
  3. Install the SDK:

    pod install
  4. Open the workspace:

    open YourApp.xcworkspace
  5. Import the SDK in your Swift files:

    import MobilePaymentSDK

CocoaPods Troubleshooting

If you encounter build issues with newer Xcode versions, add this to your Podfile:

post_install do |installer|
  installer.pods_project.targets.each do |t|
    t.build_configurations.each do |config|
      config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
    end
  end
  installer.aggregate_targets.each do |t|
    t.user_project.native_targets.each do |nt|
      nt.build_configurations.each do |config|
        config.build_settings['ENABLE_USER_SCRIPT_SANDBOXING'] = 'NO'
      end
    end
  end
end

Swift Package Manager

Via Xcode (Recommended)

  1. In Xcode, go to File > Add Package Dependencies
  2. Enter the repository URL: https://github.com/Telr-PG/telr-sdk-ios
  3. Select version rule (e.g., "Up to Next Major") and pick release (e.g., 0.0.20)
  4. Add the product MobilePaymentSDK to your target

Via Package.swift

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "YourApp",
    platforms: [
        .iOS(.v15)
    ],
    dependencies: [
        .package(url: "https://github.com/Telr-PG/telr-sdk-ios.git", .upToNextMajor(from: "0.0.20"))
    ],
    targets: [
        .target(
            name: "YourApp",
            dependencies: [
                .product(name: "MobilePaymentSDK", package: "telr-sdk-ios")
            ]
        )
    ]
)

Carthage

  1. Add to Cartfile:

    binary "https://raw.githubusercontent.com/Telr-PG/telr-sdk-ios/main/MobilePaymentSDK.json" ~> 0.0.20
  2. Update dependencies:

    carthage update --use-xcframeworks --platform iOS
  3. Add to Xcode:

    • Drag Carthage/Build/MobilePaymentSDK.xcframework into your project
    • Set to "Embed & Sign" in Frameworks, Libraries, and Embedded Content

Manual Installation

  1. Download the framework:

    • Download MobilePaymentSDK.xcframework.zip from Releases
    • Unzip to get MobilePaymentSDK.xcframework
  2. Add to Xcode:

    • Drag the framework into your project navigator
    • Check "Copy items if needed" and select your target
    • Set to "Embed & Sign" in project settings

Quick Start

Basic Implementation

import SwiftUI
import MobilePaymentSDK

struct PaymentView: View {
    @State private var showPayment = false
    private let paymentSDK = PaymentSDK()
    
    var body: some View {
        VStack {
            Button("Pay Now") {
                showPayment = true
            }
        }
        .fullScreenCover(isPresented: $showPayment) {
            paymentSDK.paymentView(
                tokenURL: "https://api.telr.com/token",
                orderURL: "https://api.telr.com/order/123",
                onFinish: { response in
                    if response.success {
                        print("Payment successful: \(response.message)")
                    } else {
                        print("Payment failed: \(response.message)")
                    }
                    showPayment = false
                }
            )
        }
    }
}

UIKit Integration

import UIKit
import MobilePaymentSDK

class PaymentViewController: UIViewController {
    private let paymentSDK = PaymentSDK()
    
    func showPayment() {
        let paymentView = paymentSDK.paymentView(
            tokenURL: "https://api.telr.com/token",
            orderURL: "https://api.telr.com/order/123",
            onFinish: { response in
                DispatchQueue.main.async {
                    if response.success {
                        self.showAlert(title: "Success", message: response.message)
                    } else {
                        self.showAlert(title: "Error", message: response.message)
                    }
                }
            }
        )
        
        let hostingController = UIHostingController(rootView: paymentView)
        present(hostingController, animated: true)
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Configuration

Basic Configuration

let configuration = PaymentSDKConfiguration(
    maxRetries: 1,
    debugLoggingEnabled: false,
    preferredLanguageCode: "en"
)

let paymentSDK = PaymentSDK(configuration: configuration)

Builder Pattern Configuration

let configuration = PaymentSDKConfiguration.builder()
    .withMaxRetries(1)
    .withDebugLoggingEnabled(true) // Enable for development
    .withPreferredLanguageCode("ar") // Arabic support
    .build()

let paymentSDK = PaymentSDK(configuration: configuration)

Configuration Options

ParameterTypeDefaultDescription
maxRetriesInt1Maximum retry attempts for failed requests
debugLoggingEnabledBoolfalseEnable debug logging (development only)
preferredLanguageCodeString?nilOverride language ("en", "ar")

Runtime Configuration Updates

// Update configuration at runtime
let newConfig = PaymentSDKConfiguration.builder()
    .withPreferredLanguageCode("ar")
    .build()

paymentSDK.updateConfiguration(newConfig)

Note: Views already created with paymentView will not automatically pick up configuration changes. Create a new view after updating configuration if you need the new config applied.

Payment Methods

Supported Payment Methods

The SDK supports the following payment methods:

  1. Credit/Debit Cards

    • Visa, Mastercard, American Express
    • 3D Secure authentication
    • Card tokenization for future payments
  2. Apple Pay

    • Native Apple Pay integration
    • Touch ID/Face ID authentication
    • Secure element processing

Card Payment Features

New Card Payment

  • Real-time card validation
  • BIN lookup for card scheme detection
  • CVV and expiry validation
  • Cardholder name validation

Saved Cards

  • Tokenized card storage
  • Quick payment with saved cards
  • Card management (view/delete)

3D Secure Authentication

  • Automatic 3DS challenge handling
  • WebView-based authentication flow
  • Seamless user experience

Apple Pay Integration

Apple Pay is automatically available when:

  • Device supports Apple Pay
  • User has cards in Wallet
  • Merchant has Apple Pay enabled

The SDK handles Apple Pay availability detection and presents the option when appropriate.

Error Handling

SDK Response Format

All payment operations return a standardized response:

public struct SDKPaymentResponse {
    public let success: Bool
    public let message: String
    public let errorCode: String?
}

Common Error Scenarios

Network Errors

.onFinish { response in
    if !response.success {
        switch response.errorCode {
        case "NETWORK_ERROR":
            // Handle network connectivity issues
            break
        case "TIMEOUT":
            // Handle request timeout
            break
        case "SERVER_ERROR":
            // Handle server-side errors
            break
        default:
            // Handle other errors
            break
        }
    }
}

Payment Failures

.onFinish { response in
    if !response.success {
        // Payment failed
        print("Payment failed: \(response.message)")
        print("Error code: \(response.errorCode ?? "Unknown")")
        
        // Show user-friendly error message
        showErrorAlert(message: response.message)
    } else {
        // Payment successful
        print("Payment successful: \(response.message)")
        showSuccessAlert(message: response.message)
    }
}

Error Codes Reference

Error CodeDescriptionAction Required
NETWORK_ERRORNetwork connectivity issueCheck internet connection
TIMEOUTRequest timeoutRetry payment
SERVER_ERRORServer-side errorContact support
INVALID_TOKENAuthentication failedRefresh token
PAYMENT_DECLINEDCard declinedTry different payment method
3DS_FAILED3D Secure authentication failedRetry payment
USER_CANCELLEDUser cancelled paymentNo action required

Internationalization

Supported Languages

  • English (Base) - Default
  • Arabic (ar) - RTL support

Language Configuration

Automatic Language Detection

By default, the SDK follows the host app's preferred language:

let paymentSDK = PaymentSDK() // Uses system language

Manual Language Override

let configuration = PaymentSDKConfiguration.builder()
    .withPreferredLanguageCode("ar") // Force Arabic
    .build()

let paymentSDK = PaymentSDK(configuration: configuration)

RTL Support

The SDK fully supports right-to-left (RTL) languages:

  • Automatic layout direction detection
  • Proper text alignment
  • Icon and image mirroring
  • Form field positioning

Troubleshooting

Common Issues

1. "No such module 'MobilePaymentSDK'"

Causes:

  • Framework not properly linked
  • Wrong import statement
  • Build configuration issue

Solutions:

  • Ensure framework is added to "Frameworks, Libraries, and Embedded Content"
  • Check that you're importing MobilePaymentSDK (not TelrSDK)
  • Clean build folder and rebuild

2. "Image not found" at Runtime

Causes:

  • Framework not embedded
  • Architecture mismatch

Solutions:

  • Set framework to "Embed & Sign" in project settings
  • Ensure you're using the correct XCFramework for your target architecture

3. CocoaPods Build Errors

Causes:

  • Script sandboxing enabled
  • Derived data corruption
  • Pod cache issues

Solutions:

# Clean derived data
rm -rf ~/Library/Developer/Xcode/DerivedData/*

# Clean pod cache
pod cache clean --all

# Reinstall pods
pod deintegrate
pod install

4. Carthage Framework Issues

Causes:

  • Not using XCFrameworks
  • Framework not properly embedded

Solutions:

# Ensure XCFrameworks are used
carthage update --use-xcframeworks --platform iOS

# Verify framework is embedded
# Check project settings > Frameworks, Libraries, and Embedded Content

5. Payment Not Processing

Causes:

  • Invalid URLs
  • Network connectivity
  • Server configuration

Solutions:

  • Verify token and order URLs are correct
  • Check network connectivity
  • Enable debug logging to see detailed error messages
  • Verify server endpoints are accessible

Debug Logging

Enable debug logging for development:

let configuration = PaymentSDKConfiguration.builder()
    .withDebugLoggingEnabled(true)
    .build()

let paymentSDK = PaymentSDK(configuration: configuration)

Debug logs will show:

  • Network requests and responses
  • Error details
  • SDK internal state
  • Validation results

Performance Optimization

Memory Management

  • The SDK automatically manages memory for payment views
  • Views are released when payment completes or is cancelled
  • No manual cleanup required

Network Optimization

  • Requests are automatically retried on failure
  • Timeout intervals are configurable

API Reference

PaymentSDK

Main SDK class for payment processing.

public class PaymentSDK {
    public init(configuration: PaymentSDKConfiguration = PaymentSDKConfiguration())
    public func updateConfiguration(_ configuration: PaymentSDKConfiguration)
    
    @MainActor
    public func paymentView(
        tokenURL: String,
        orderURL: String,
        onFinish: @escaping (SDKPaymentResponse) -> Void
    ) -> some View
}

PaymentSDKConfiguration

Configuration class for SDK customization.

public struct PaymentSDKConfiguration {
    public let maxRetries: Int
    public let debugLoggingEnabled: Bool
    public let preferredLanguageCode: String?
    
    public static func builder() -> Builder
    public init(builder configure: (input Builder) -> Void)
}

SDKPaymentResponse

Response object for payment operations.

public struct SDKPaymentResponse {
    public let success: Bool
    public let message: String
    public let errorCode: String?
    
    public static func success(message: String) -> SDKPaymentResponse
    public static func failure(message: String, errorCode: String?) -> SDKPaymentResponse
}

Order Model

Order information structure.

public struct Order {
    public let ref: String
    public let amount: Amount
    public let status: OrderStatus
    public let allowedPaymentMethods: [PaymentMethod]?
    public let _links: OrderLinks?
    public let payments: [PaymentResponse]?
}

Amount Model

Payment amount structure.

public struct Amount {
    public let value: String
    public let currency: String?
}

PaymentMethod

Supported payment method entry returned with an order.

public struct PaymentMethod {
    public let schemes: [String]          // e.g. ["VISA", "MASTERCARD"]
    public let type: PaymentMethodType    // .card, .applePay
}

PaymentMethodType

Type of payment method available for the order.

public enum PaymentMethodType: String {
    case card = "CARD"
    case applePay = "APPLE_PAY"
}

OrderStatus

High-level status of the order.

public enum OrderStatus: String {
    case pending = "PENDING"
    case authorised = "AUTHORISED"
    case paid = "PAID"
    case cancelled = "CANCELLED"
    case declined = "DECLINED"
}

OrderLinks (SDK-internal)

Links to operations/endpoints associated with the order (card, Apple Pay, 3DS, etc.). These are used internally by the SDK.